Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何实现ItemTemplate和ItemsSource的可附加属性_C#_Wpf_Datatemplate_Attached Properties_Wpf Grid - Fatal编程技术网

C# 如何实现ItemTemplate和ItemsSource的可附加属性

C# 如何实现ItemTemplate和ItemsSource的可附加属性,c#,wpf,datatemplate,attached-properties,wpf-grid,C#,Wpf,Datatemplate,Attached Properties,Wpf Grid,为了创建一个可伸缩的钢琴键盘,我正在尝试使用WPF网格作为ItemsControl,使用附加属性。键盘上的每个键可能跨越1到3列,这取决于它的前后顺序,如果是尖锐的,则跨越1行;如果是自然的,则跨越2行。我已经有两个附加属性用于动态设置网格的列数和行数(尽管这些属性需要调整以支持每列/行的宽度/高度设置) 我现在需要实现的是ItemsSource(键)和ItemTemplate(PianoKeyView)的两个可附加属性。我需要在网格控件上使用它,因为ItemsControl只支持Uniform

为了创建一个可伸缩的钢琴键盘,我正在尝试使用WPF网格作为ItemsControl,使用附加属性。键盘上的每个键可能跨越1到3列,这取决于它的前后顺序,如果是尖锐的,则跨越1行;如果是自然的,则跨越2行。我已经有两个附加属性用于动态设置网格的列数和行数(尽管这些属性需要调整以支持每列/行的宽度/高度设置)

我现在需要实现的是
ItemsSource
(键)和
ItemTemplate
(PianoKeyView)的两个可附加属性。我需要在网格控件上使用它,因为
ItemsControl
只支持
UniformGrid
作为其
ItemsPanel
的网格,并且不将特定项分配给特定列/行。我的钢琴键盘每八度键需要17列,但ItemsControl只能在
UniformGrid
中创建12列,因为只有12个键传递给它。我已经包括了一个1倍频程钢琴键盘的图像,包括每个所需列的索引

这是我目前的键盘代码,我缺少
GridExtensions.ItemsSource
GridExtensions.ItemTemplate
的实现
GridExtensions
是一个包含可附加属性的静态类

<UserControl x:Class="SphynxAlluro.Music.Wpf.PianoKeyboard.View.PianoKeyboardView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
         xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
         xmlns:converters="http://schemas.sphynxalluro.com/converters"
         xmlns:local="clr-namespace:SphynxAlluro.Music.Wpf.PianoKeyboard.View"
         xmlns:prism="http://www.codeplex.com/prism"
         xmlns:sphynxAlluroControls="http://schemas.sphynxalluro.com/controls"
         xmlns:wpfBindingExtensions="http://schemas.sphynxalluro.com/bindingExtensions"
         mc:Ignorable="d"
         d:DesignHeight="200" d:DesignWidth="600">
<UserControl.Resources>
    <converters:KeysToColumnsCountConverter x:Key="keysToColumnsCountConverter"/>
    <converters:KeysToRowsCountConverter x:Key="keysToRowsCountConverter"/>
    <converters:IsSharpToRowSpanConverter x:Key="isSharpToRowSpanConverter"/>
    <converters:KeysCollectionAndKeyToColumnIndexConverter x:Key="keysCollectionAndKeyToColumnIndexConverter"/>
    <converters:KeysCollectionAndKeyToColumnSpanConverter x:Key="keysCollectionAndKeyToColumnSpanConverter"/>
</UserControl.Resources>
<Grid wpfBindingExtensions:GridExtensions.ItemsSource="{Binding Keys}"
      wpfBindingExtensions:GridExtensions.ItemsOrientation="Horizontal"
      wpfBindingExtensions:GridExtensions.ColumnCount="{Binding Keys, Converter={StaticResource keysToColumnsCountConverter}}"
      wpfBindingExtensions:GridExtensions.RowCount="{Binding Keys, Converter={StaticResource keysToRowsCountConverter}}">
    <wpfBindingExtensions:GridExtensions.ItemTemplate>
        <DataTemplate>
            <local:PianoKeyView Grid.RowSpan="{Binding Note.IsSharp, Mode=OneTime, Converter={StaticResource isSharpToRowSpanConverter}}"
                            DataContext="{Binding}">
                <Grid.Column>
                    <MultiBinding Converter="{StaticResource keysCollectionAndKeyToColumnIndexConverter}" Mode="OneTime">
                        <Binding RelativeSource="{RelativeSource AncestorType=ItemsControl}" Path="Items"/>
                        <Binding/>
                    </MultiBinding>
                </Grid.Column>
                <Grid.ColumnSpan>
                    <MultiBinding Converter="{StaticResource keysCollectionAndKeyToColumnSpanConverter}" Mode="OneTime">
                        <Binding RelativeSource="{RelativeSource AncestorType=ItemsControl}" Path="Items"/>
                        <Binding/>
                    </MultiBinding>
                </Grid.ColumnSpan>
            </local:PianoKeyView>
        </DataTemplate>
    </wpfBindingExtensions:GridExtensions.ItemTemplate>
</Grid>

我试图通过
网格
直接实现的功能可以通过
网格
项控件
实现

结果发现缺少的部分是一个
样式
,带有
TargetType
ContentPresenter
。在这种样式中,可附加的
Grid
属性(如
Grid.RowSpan
Grid.Column
Grid.ColumnSpan
)可通过适当的转换器进行设置,这些转换器接收ItemsControl和Key的数据上下文并返回所需的整数。这里还可以设置关键点的Z索引,以便锐利的关键点出现在自然关键点之上

<ItemsControl.ItemContainerStyle>
    <Style TargetType="ContentPresenter">
        <Setter Property="Grid.RowSpan" Value="{Binding Note.IsSharp, Mode=OneTime, Converter={StaticResource isSharpToRowSpanConverter}}"/>
        <Setter Property="Grid.Column">
            <Setter.Value>
                <MultiBinding Converter="{StaticResource keysCollectionAndKeyToColumnIndexConverter}" Mode="OneTime">
                    <Binding RelativeSource="{RelativeSource AncestorType=ItemsControl}" Path="Items"/>
                    <Binding/>
                </MultiBinding>
            </Setter.Value>
        </Setter>
        <Setter Property="Grid.ColumnSpan">
            <Setter.Value>
                <MultiBinding Converter="{StaticResource keysCollectionAndKeyToColumnSpanConverter}" Mode="OneTime">
                    <Binding RelativeSource="{RelativeSource AncestorType=ItemsControl}" Path="Items"/>
                    <Binding/>
                </MultiBinding>
            </Setter.Value>
        </Setter>
        <Setter Property="Panel.ZIndex" Value="{Binding Note.IsSharp, Converter={StaticResource booleanToIntegerConverter}}"/>
    </Style>
</ItemsControl.ItemContainerStyle>
我修改了原始的
ColumnCount
RowCount
可附加属性,改为根据需要使用
IEnumerable
/
IEnumerable
,以便每个列/行的大小也可以传递给属性(在本例中,我是通过一个转换器执行此操作的,该转换器接收所有PianokeyViewModel,并为每个PianokeyViewModel返回一个具有适当星形大小的ColumnDefinition/RowDefinition。RowDefinition的分配仅适用于边缘情况,在这种情况下,键盘中只需要自然键或锐键(例如,B到C,e到F或单个锐键或自然键)。RowDefinitions attached属性的代码与ColumnDefinitions属性的逻辑基本相同(仅使用RowDefinitions而不是ColumnDefinitions),因此我将在此处发布ColumnDefinitions的代码:

public static class GridExtensions
{
    // Using a DependencyProperty as the backing store for ColumnDefinitions.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ColumnDefinitionsProperty =
        DependencyProperty.RegisterAttached(
            nameof(ColumnDefinitionsProperty).Substring(0, nameof(ColumnDefinitionsProperty).Length - "Property".Length),
            typeof(IEnumerable<ColumnDefinition>),
            typeof(GridExtensions),
            new PropertyMetadata(Enumerable.Empty<ColumnDefinition>(), ColumnDefinitionsChanged));

    public static IEnumerable<ColumnDefinition> GetColumnDefinitions(DependencyObject obj)
        => (IEnumerable<ColumnDefinition>)obj.GetValue(ColumnDefinitionsProperty);

    public static void SetColumnDefinitions(DependencyObject obj, IEnumerable<ColumnDefinition> value)
        => obj.SetValue(ColumnDefinitionsProperty, value);

    private static void ColumnDefinitionsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var columnDefinitionCollection = ((Grid)d).ColumnDefinitions;
        var newColumnDefinitions = (IEnumerable<ColumnDefinition>)e.NewValue;
        var columnCount = newColumnDefinitions.Count();

        columnDefinitionCollection.Clear();

        foreach (var newColumnDefinition in newColumnDefinitions)
            columnDefinitionCollection.Add(newColumnDefinition);
    }
}
公共静态类GridExtensions
{
//使用DependencyProperty作为列定义的后备存储。这将启用动画、样式设置、绑定等。。。
公共静态只读从属属性列定义属性=
DependencyProperty.RegisterAttached(
nameof(ColumnDefinitionsProperty).Substring(0,nameof(ColumnDefinitionsProperty).Length—“属性”.Length),
类型(IEnumerable),
类型(网格扩展),
新的PropertyMetadata(Enumerable.Empty(),ColumnDefinitionsChanged));
公共静态IEnumerable GetColumnDefinitions(DependencyObject obj)
=>(IEnumerable)对象GetValue(ColumnDefinitionsProperty);
公共静态void SetColumnDefinitions(DependencyObject对象,IEnumerable值)
=>对象设置值(列定义属性、值);
私有静态无效列定义更改(DependencyObject d、DependencyPropertyChangedEventArgs e)
{
var columnDefinitionCollection=((网格)d).ColumnDefinitions;
var newColumnDefinitions=(IEnumerable)e.NewValue;
var columnCount=newColumnDefinitions.Count();
columnDefinitionCollection.Clear();
foreach(newColumnDefinition中的var newColumnDefinition)
columnDefinitionCollection.Add(新建ColumnDefinition);
}
}
有关附加属性的更多信息,请参阅“使用网格作为ItemsControl的面板”(Using a Grid as Panel For a ItemsControl)(),我从中找到了衍生上述静态类的原始代码。否则,这里的关键元素是:

  • 将可附加的面板属性(例如
    Grid.Column
    panel.ZOrder
    )指定给
    ItemsControl.itemscontainerstyle
    中的
    ContentPresenter
    样式
  • ItemsControl.ItemsPanel
    设置为
    Grid
    ItemsPanelTemplate
    ,并从此处设置网格的
    列定义和
    行定义
  • 我没有在这里发布我所有的转换器,因为这个答案已经很长了,但是有人让我知道他们是否觉得它们与post相关。否则,这就是最终结果


    5个转换器和4个附加DP?我想说的是,与其使视图更加复杂,不如简化视图模型,使数据的形式更便于在ItemsControl中绑定。另一个选项是在ItemsPanelTemplate中使用水平堆叠面板,并通过style/binding/whate使用负边距和Panel.ZIndexver使非相邻白键延伸并与“下方”相交黑键。@Ed Plunkett@ASh我想使用ItemsControl,但如何为12个项目生成17列?我最初将其作为
    ItemsControl
    使用,但
    PianoKey
    上可附加的
    Grid.Column
    Grid.Row
    属性与
    UniformGrid
    不起作用,就我而言记得吗<
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <local:PianoKeyView DataContext="{Binding}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Grid wpfBindingExtensions:GridExtensions.ColumnDefinitions="{Binding Keys, Converter={StaticResource keysToColumnDefinitionsConverter}}"
                  wpfBindingExtensions:GridExtensions.RowDefinitions="{Binding Keys, Converter={StaticResource keysToRowDefinitionsConverter}}"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    
    public static class GridExtensions
    {
        // Using a DependencyProperty as the backing store for ColumnDefinitions.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ColumnDefinitionsProperty =
            DependencyProperty.RegisterAttached(
                nameof(ColumnDefinitionsProperty).Substring(0, nameof(ColumnDefinitionsProperty).Length - "Property".Length),
                typeof(IEnumerable<ColumnDefinition>),
                typeof(GridExtensions),
                new PropertyMetadata(Enumerable.Empty<ColumnDefinition>(), ColumnDefinitionsChanged));
    
        public static IEnumerable<ColumnDefinition> GetColumnDefinitions(DependencyObject obj)
            => (IEnumerable<ColumnDefinition>)obj.GetValue(ColumnDefinitionsProperty);
    
        public static void SetColumnDefinitions(DependencyObject obj, IEnumerable<ColumnDefinition> value)
            => obj.SetValue(ColumnDefinitionsProperty, value);
    
        private static void ColumnDefinitionsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var columnDefinitionCollection = ((Grid)d).ColumnDefinitions;
            var newColumnDefinitions = (IEnumerable<ColumnDefinition>)e.NewValue;
            var columnCount = newColumnDefinitions.Count();
    
            columnDefinitionCollection.Clear();
    
            foreach (var newColumnDefinition in newColumnDefinitions)
                columnDefinitionCollection.Add(newColumnDefinition);
        }
    }