创建附加属性以保存滚动条标记的位置

时间:2014-06-12 06:28:48

标签: c# wpf xaml wpfdatagrid attached-properties

我为DataGrid创建了一个略微自定义的垂直滚动条。在其中我添加了一个ItemsControl来保存所选项目的位置。这是迄今为止使用硬编码标记的模型。 enter image description here

下面是我的自定义垂直滚动条模板,其中ItemsControl放置了硬编码标记值。

<ControlTemplate x:Key="VertScrollBar" TargetType="{x:Type ScrollBar}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition MaxHeight="18" />
            <RowDefinition Height="0.00001*" />
            <RowDefinition MaxHeight="18" />
        </Grid.RowDefinitions>
        <Border Grid.RowSpan="3" CornerRadius="2" Background="#F0F0F0" />
        <RepeatButton Grid.Row="0" Style="{StaticResource ScrollBarLineButton}" Height="18" Command="ScrollBar.LineUpCommand" Content="M 0 4 L 8 4 L 4 0 Z" />
        <Track x:Name="PART_Track" Grid.Row="1" IsDirectionReversed="true">
            <Track.DecreaseRepeatButton>
                <RepeatButton Style="{StaticResource ScrollBarPageButton}" Command="ScrollBar.PageUpCommand" />
            </Track.DecreaseRepeatButton>
            <Track.Thumb>
                <Thumb Style="{StaticResource ScrollBarThumb}" Margin="1,0,1,0">
                    <Thumb.BorderBrush>
                        <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                            <LinearGradientBrush.GradientStops>
                                <GradientStopCollection>
                                    <GradientStop Color="{DynamicResource BorderLightColor}" Offset="0.0" />
                                    <GradientStop Color="{DynamicResource BorderDarkColor}" Offset="1.0" />
                                </GradientStopCollection>
                            </LinearGradientBrush.GradientStops>
                        </LinearGradientBrush>
                    </Thumb.BorderBrush>
                    <Thumb.Background>
                        <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                            <LinearGradientBrush.GradientStops>
                                <GradientStopCollection>
                                    <GradientStop Color="{DynamicResource ControlLightColor}" Offset="0.0" />
                                    <GradientStop Color="{DynamicResource ControlMediumColor}" Offset="1.0" />
                                </GradientStopCollection>
                            </LinearGradientBrush.GradientStops>
                        </LinearGradientBrush>
                    </Thumb.Background>
                </Thumb>
            </Track.Thumb>
            <Track.IncreaseRepeatButton>
                <RepeatButton Style="{StaticResource ScrollBarPageButton}" Command="ScrollBar.PageDownCommand" />
            </Track.IncreaseRepeatButton>
        </Track>
        <!-- BEGIN -->
        <ItemsControl Grid.Column="0" VerticalAlignment="Stretch" Name="ItemsSelected">
            <sys:Double>30</sys:Double>
            <sys:Double>70</sys:Double>
            <sys:Double>120</sys:Double>
            <sys:Double>170</sys:Double>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Rectangle Fill="SlateGray" Width="18" Height="4"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemContainerStyle>
                <Style TargetType="ContentPresenter">
                    <Setter Property="Canvas.Top" Value="{Binding}" />
                </Style>
            </ItemsControl.ItemContainerStyle>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
        <!-- END -->
        <RepeatButton Grid.Row="3" Style="{StaticResource ScrollBarLineButton}" Height="18" Command="ScrollBar.LineDownCommand" Content="M 0 0 L 4 4 L 8 0 Z" />
    </Grid>
</ControlTemplate>

我接下来要做的是创建一个AttachedProperty来保存标记位置并将其绑定回ItemsControl。

我真正不理解的是:
- 这个附加属性Type应该是什么,int的ObservableCollection?
- 由于这是DataGrid中所选项目总数的指南,是否需要以某种方式缩放标记的位置?
- 我有一个捕获DataGrid.SelectionChanged的附加行为,但是如果主集合发生变化那么它似乎不是一个事件呢?

[编辑]

直接绑定到DataGrids SelectedItems。 (但是当选择某些内容时,ItemsControl顶部会出现闪烁)
- 删除或注释掉SelectionChanged行为 - 将ItemSource更改为:

ItemsSource="{Binding ElementName=GenericDataGrid, Path=SelectedItems}"

- 将多重绑定更改为:

<MultiBinding Converter="{StaticResource MarkerPositionConverter}">
    <Binding/>
    <Binding RelativeSource="{RelativeSource AncestorType={x:Type DataGrid}}" />
    <Binding Path="ActualHeight" ElementName="ItemsSelected"/>
    <Binding Path="Items.Count" ElementName="GenericDataGrid"/>
</MultiBinding>

- 最后转换为:

public class MarkerPositionConverter: IMultiValueConverter
{
    //Performs the index to translate conversion
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        //calculated the transform values based on the following
        object o = (object)values[0];
        DataGrid dg = (DataGrid)values[1];
        double itemIndex = dg.Items.IndexOf(o);
        double trackHeight = (double)values[2];
        int itemCount = (int)values[3];
        double translateDelta = trackHeight / itemCount;
        return itemIndex * translateDelta;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

1 个答案:

答案 0 :(得分:2)

我试图达到你想要的结果

所以我做了一些修改,我已经评论了我做了哪些更改

将此转换器引用添加到资源

    <helpers:MarkerPositionConverter x:Key="MarkerPositionConverter"/>

项目控制显示标记的xaml

    <!-- added Grid.Row="1", removed other attributes, removed the ItemsControlBeahviors, not much needed-->
    <ItemsControl Grid.Row="1" Name="ItemsSelected"
                  ItemsSource="{Binding Source={x:Static helpers:MyClass.Instance}, Path=SelectedMarkers}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <!--you can optionally bind height to scale accordingly if needed-->
                <Rectangle Fill="#99708090" Width="18" Height="4">
                    <Rectangle.RenderTransform>
                        <!--added a translate transform-->
                        <TranslateTransform>
                            <TranslateTransform.Y>
                                <!--multi binded Y to the item and the actual height of MarkerItems control using the new MarkerPositionConverter-->
                                <MultiBinding Converter="{StaticResource MarkerPositionConverter}">
                                    <Binding/>
                                    <Binding Path="ActualHeight" ElementName="ItemsSelected"/>
                                </MultiBinding>
                            </TranslateTransform.Y>
                        </TranslateTransform>
                    </Rectangle.RenderTransform>
                </Rectangle>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>

behavior.cs

public class DataGridBehaviors : Behavior<DataGrid>
{
    ...

    void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        MyClass.Instance.SelectedMarkers.Clear();
        //updated item count
        MyClass.Instance.ItemCount = this.AssociatedObject.Items.Count;
        foreach (object o in this.AssociatedObject.SelectedItems)
            MyClass.Instance.SelectedMarkers.Add(this.AssociatedObject.Items.IndexOf(o));
    }
}

//removed ItemsControlBeahviors

public class MyClass : INotifyPropertyChanged
{
    ...

    //added item count property
    public int ItemCount { get; set; }

    ...
}

//added class to perform the index to translate conversion
public class MarkerPositionConverter: IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        //calculated the transform values based on the following
        double itemIndex = (double)values[0];
        double trackHeight = (double)values[1];
        double translateDelta = trackHeight / MyClass.Instance.ItemCount;
        return itemIndex * translateDelta;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

现在您可以根据需要自定义

删除闪烁

闪烁是由于矩形的初始放置以及绑定获取其所有值之前所以为了避免这种初始间歇性闪烁使用此

    <!--added fallback value to avoid intermittent value-->
    <MultiBinding Converter="{StaticResource MarkerPositionConverter}" FallbackValue="-1000">
        <Binding/>
        <Binding RelativeSource="{RelativeSource AncestorType={x:Type DataGrid}}" />
        <Binding Path="ActualHeight" ElementName="ItemsSelected"/>
        <Binding Path="Items.Count" ElementName="GenericDataGrid"/>
    </MultiBinding>

因此,当任何绑定属性忙于解析值或没有任何值时,我将矩形推出视图1000 px。

和项目面板模板(可选)

    <ItemsPanelTemplate>
        <!--added ClipToBounds to be extra safe-->
        <Canvas ClipToBounds="True"/>
    </ItemsPanelTemplate>

由于默认情况下画布不剪切其子画面,因此将ClipToBounds设置为true是安全的。即使在使用巨大的后备值之后,当UI中某处仍然可以看到闪烁时,这是必要的。