我正在尝试使用 WPF Grid 作为 ItemsControl 使用附加属性来创建可扩展的钢琴键盘。键盘中的每个键可以跨越 1 到 3 列,具体取决于它之前和之后的内容,如果是锐利的,则跨越 1 行,如果是自然的,则跨越 2 行。我已经有 2 个附加属性用于动态设置网格的列数和行数(尽管需要调整这些属性以支持每列/行的宽度/高度的设置)。
我现在需要实现的是ItemsSource
(Keys) 和ItemTemplate
(PianoKeyView) 的两个可附加属性。我需要在 Grid 控件上使用它,因为ItemsControl
它只支持UniformGrid
作为 GridItemsPanel
并且不将特定项目分配给特定的列/行。我的钢琴键盘每个八度音阶需要 17 列键,但 ItemsControl 只会在 a 中创建 12 列,UniformGrid
因为只有 12 个键传递给它。我已经包含了一个 1 倍频程钢琴键盘的图像,其中包含每个所需列的索引。
这是我目前的键盘代码,我缺少GridExtensions.ItemsSource
and的实现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>
这是 GridExtensions 中 ItemTemplate 可附加属性的 ItemTemplateChanged 处理程序的代码,请注意不编译的行上方的两个 TODO。
private static void ItemTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var itemTemplate = (DataTemplate)e.NewValue;
var itemsSource = GetItemsSource(d);
var itemsSourceCount = itemsSource.Count();
var itemsOrientation = GetItemsOrientation(d);
var gridChildren = ((Grid)d).Children;
gridChildren.Clear();
switch (itemsOrientation)
{
case Orientation.Horizontal:
foreach (var item in itemsSource)
{
var itemFactory = new FrameworkElementFactory(item.GetType());
//TODO: Find out where the ContentProperty for Grid is.
itemFactory.SetValue(d.ContentProperty, item);
itemTemplate.VisualTree = itemFactory;
//TODO: Find out how to add the applied itemTemplate.
gridChildren.Add(itemTemplate);
}
break;
case Orientation.Vertical:
break;
default:
throw new EnumValueNotSupportedException(itemsOrientation, nameof(itemsOrientation).ToPascalCase());
}
}