统一网格作为ItemsControl中除First之外的所有项目的面板模板

use*_*095 5 c# wpf user-interface xaml itemscontrol

我正在研究进度向导.我将它定义为基于ItemsControl的样式.我有一个带有两个DataTemplates的ItemTemplateSelector,一个用于第一个项目,另一个用于其余项目.我有正确的工作,除了一个非常棘手的修复问题.第一项和第二项之间存在差距.在此输入图像描述 这是控件应该是这样的: 在此输入图像描述之所以出现这种差距,是因为我使用的是统一网格,所以所有列的大小都相同,即使第一列没有线.使用统一网格很重要,因为我想要一行中的所有内容,并且我希望控件在其增长时伸展以填充可用空间.我已经尝试过不使用统一网格,但最终我遇到边缘问题或者没有填充可用空间.我该如何解决这个差距呢?

这是代码:

<Style x:Key="WizardProgressBar" TargetType="{x:Type ItemsControl}">
        <Style.Resources>
            <DataTemplate x:Key="FirstItem">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <Ellipse Name="ellipse"  HorizontalAlignment="Left" Height="32" Width="32"  />
                </Grid>
                <DataTemplate.Triggers>
                    <DataTrigger Binding="{Binding Completed}" Value="False">
                        <Setter TargetName="ellipse" Property="Stroke" Value="{DynamicResource DisabledBrush}" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding InProgress}" Value="True">
                        <Setter TargetName="ellipse" Property="Stroke" Value="{DynamicResource PrimaryTextBrush}"/>
                    </DataTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>


            <DataTemplate x:Key="OtherItem">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <Ellipse Name="ellipse"  Grid.Column="1" HorizontalAlignment="Left" Height="32" Width="32" />
                    <Line Name="leftPath" Grid.Column="0" X1="0" Y1="16" 
                              X2="{Binding ActualWidth, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}}" Y2="16" />
                </Grid>
                <DataTemplate.Triggers>
                    <DataTrigger Binding="{Binding Completed}" Value="False">
                        <Setter TargetName="ellipse" Property="Stroke" Value="{DynamicResource DisabledBrush}" />
                        <Setter TargetName="leftPath" Property="Stroke" Value="{DynamicResource DisabledBrush}"/>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding InProgress}" Value="True">
                        <Setter TargetName="ellipse" Property="Stroke" Value="{DynamicResource PrimaryTextBrush}"/>
                        <Setter TargetName="leftPath" Property="Stroke" Value="{DynamicResource PrimaryTextBrush}"/>
                    </DataTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>
        </Style.Resources>
        <Setter Property="ItemsPanel">
            <Setter.Value>
                <ItemsPanelTemplate>
                    <UniformGrid Rows="1"/>
                </ItemsPanelTemplate>
            </Setter.Value>
        </Setter>
        <Setter Property="ItemTemplateSelector">
            <Setter.Value>
                <wpf:ItemsDataTemplateSelector  FirstItem="{StaticResource FirstItem}" OtherItem="{StaticResource OtherItem}" />
            </Setter.Value>
        </Setter>
    </Style>
Run Code Online (Sandbox Code Playgroud)


public class ItemsDataTemplateSelector : DataTemplateSelector
    {
        public DataTemplate FirstItem { get; set; }
        public DataTemplate OtherItem { get; set; }

        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            var itemsControl = ItemsControl.ItemsControlFromItemContainer(container);
            var returnTemplate = (itemsControl.ItemContainerGenerator.IndexFromContainer(container) == 0) ? FirstItem : OtherItem;
            return returnTemplate;
        }
    }
Run Code Online (Sandbox Code Playgroud)

dym*_*oid 1

主要问题是设置中的第一个项目实际上必须具有与其他项目不同的宽度。这对于UniformGrid.

\n\n

我可以向您建议以下解决方案。

\n\n

目标配置将如下所示:

\n\n
|\xc2\xb7\xc2\xb7O\xe2\x80\x93\xe2\x80\x93|\xe2\x80\x93\xe2\x80\x93O\xe2\x80\x93\xe2\x80\x93|\xe2\x80\x93\xe2\x80\x93O\xe2\x80\x93\xe2\x80\x93|\xe2\x80\x93\xe2\x80\x93O\xc2\xb7\xc2\xb7|\n
Run Code Online (Sandbox Code Playgroud)\n\n

您将在左侧和右侧有半个单元宽的边距(由上面的点表示)。如果需要,您可以通过指定控件的边距来利用它们。

\n\n

此外,我们还可以简化您的数据模板。实际上,我们不需要第一个项目和其他项目的单独模板。见下文。

\n\n

设置

\n\n

我们将在这里使用三个技巧:

\n\n
    \n
  • ItemsControl.AlternationIndex用于获取第一个元素的属性ItemsControl
  • \n
  • ACanvasUniforGrid允许在相应的单元格之外绘制连接线
  • \n
  • 一个特别的IValueConverter可以帮助我们计算所需的线位置
  • \n
\n\n

现在,这就是适合您的风格ItemsControl

\n\n
<Style x:Key="WizardProgressBar" TargetType="{x:Type ItemsControl}">\n  <Style.Resources>\n    <local:LinearConverter x:Key="Multiplier" Scale="-0.5" Offset="16"/>\n\n    <DataTemplate DataType="{x:Type local:YourItemType}">\n      <Grid>            \n        <Canvas>\n          <Rectangle x:Name="leftPath" Height="2" Stroke="Blue" Canvas.Top="16"\n                     Canvas.Left="{Binding Width, RelativeSource={RelativeSource Self}, Converter={StaticResource Multiplier}}"\n                     Width="{Binding ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}}"/>\n        </Canvas>\n        <Ellipse Name="ellipse" HorizontalAlignment="Center" Height="32" Width="32" Stroke="Blue"/>\n      </Grid>\n      <DataTemplate.Triggers>\n        <Trigger Property="ItemsControl.AlternationIndex" Value="0">\n          <Setter TargetName="leftPath" Property="Visibility" Value="Collapsed"/>\n        </Trigger>\n        <DataTrigger Binding="{Binding Completed}" Value="False">\n          <Setter TargetName="ellipse" Property="Stroke" Value="{DynamicResource DisabledBrush}" />\n          <Setter TargetName="leftPath" Property="Stroke" Value="{DynamicResource DisabledBrush}" />\n        </DataTrigger>\n        <DataTrigger Binding="{Binding InProgress}" Value="True">\n          <Setter TargetName="ellipse" Property="Stroke" Value="{DynamicResource PrimaryTextBrush}"/>\n          <Setter TargetName="leftPath" Property="Stroke" Value="{DynamicResource PrimaryTextBrush}" />\n        </DataTrigger>\n      </DataTemplate.Triggers>\n    </DataTemplate>\n  </Style.Resources>\n  <Setter Property="ItemsPanel">\n    <Setter.Value>\n      <ItemsPanelTemplate>\n        <UniformGrid Rows="1"/>\n      </ItemsPanelTemplate>\n    </Setter.Value>\n  </Setter>\n  <Setter Property="AlternationCount" Value="100"/>\n</Style>\n
Run Code Online (Sandbox Code Playgroud)\n\n

请注意以下更改:

\n\n
    \n
  • 只有一个DataTemplate,不需要两个不同的
  • \n
  • 不再需要模板选择器
  • \n
  • 我们需要一个符合我们风格的特殊转换器(代码如下)
  • \n
  • Ellipse线(作为 a Rectangle)放置在 1x1 中Grid同一单元格中
  • \n
  • 该线路本身位于Canvas
  • \n
  • Ellipse水平
  • \n
  • 还有一个附加项Trigger可以捕获AlternationIndex值 0 并隐藏该行 - 它用于第一个元素
  • \n
  • 风格设定AlternationCount您最多有 100 个向导页面,样式将 设为 100
  • \n
\n\n

这是转换器:

\n\n
class LinearConverter : IValueConverter\n{\n    public double Scale { get; set; }\n\n    public double Offset { get; set; }\n\n    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n    {\n        // TODO: exception handling\n        return System.Convert.ToDouble(value) * Scale + Offset;\n    }\n\n    // ConvertBack just throws a NotImplementedException\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

解释

\n\n

每个Ellipse代表一个向导页面并放置在相应UniformGrid单元格的中心。这些线放置在椭圆的左侧。线条的宽度设置为UnifiormGrid单个单元格的宽度,并且它们在单元格中的水平位置Canvas根据以下公式设置:WidthOfEllipse / 2 - WidthOfCell / 2。这确保了正确的放置。

\n\n

对于第一个向导页面,该行将被隐藏。

\n\n

请注意,您可能想要使用Fill省略号来隐藏下面的线条。

\n