2017-11-03 6 views
-2

スケーラブルなピアノキーボードを作成するために、添付プロパティを使用してItemsControlとしてWPFグリッドを使用しようとしています。キーボードの各キーは、先行するものと後続するものに応じて1〜3列にわたり、シャープの場合は1行、自然の場合は2行にまたがります。グリッドの列数と行数を動的に設定するための2つのプロパティが既に用意されています(これらは各列/行の幅/高さの設定をサポートするために調整する必要があります)。ItemTemplateとItemsSourceのアタッチ可能なプロパティを実装する方法

実装する必要があるのは、ItemsSource(鍵)とItemTemplate(PianoKeyView)の2つの接続可能なプロパティです。 ItemsControlItemsPanelのグリッドとしてUniformGridしかサポートしておらず、特定の列/行に特定の項目を割り当てていないため、Gridコントロールでこれを使用する必要があります。私のピアノのキーボードは1オクターブあたり17列のキーを必要としますが、ItemsControlはUniformGridに12列しか作成しません。私は1オクターブのピアノ・キーボードのイメージを含め、必要な各列の索引を含めました。

PianoKeyboard Grid Column Indices

それが現在私がGridExtensions.ItemsSourceGridExtensions.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ハンドラのコードで、コンパイルされませんライン上の2 TODOの点に注意してください。私が直接GridItemsPanelItemsControlで達成することができGridで達成しようとしていた何

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()); 
    } 
} 
+0

5コンバータおよびDP付属の4でしたか?私はビューを複雑にするのではなく、ItemsControlのバインディングに便利な形式でデータを表示するためにビューモデルを簡素化するべきだと言います。 – ASh

+0

もう一つのオプションは、ItemsPanelTemplateで水平StackPanelを使用して負のマージンとPanel.ZIndexスタイル/バインディング/それ以外の白いキーが黒いキーの「下」に出会うようにするために使用します。 –

+0

@Ed Plunkett @ASh ItemsControlを使用したいのですが、12個の項目に対して17個の列を生成するにはどうすればよいですか?私は元々 'ItemsControl'としてこれを持っていましたが、' PianoKey'の 'Grid.Column'と' Grid.Row'プロパティは 'UniformGrid'で動作しませんでした。 'ItemsPanelTemplate'は列を生成しませんでした。 – Sphynx

答えて

0

欠けている部分がStyleで、TargetTypeContentPresenterであることが判明しました。このスタイルでは、Grid.RowSpan,Grid.ColumnおよびGrid.ColumnSpanなどのアタッチ可能なGridプロパティは、ItemsControlおよびKeyのDataContextを取り込み、必要な整数を返す適切なコンバーターを介して設定可能です。また、ここでは、鋭いキーがナチュラルキーの上に現れるように、キーの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> 

これが次に大きく、以下にItemTemplateにを簡素化:

<ItemsControl.ItemTemplate> 
    <DataTemplate> 
     <local:PianoKeyView DataContext="{Binding}"/> 
    </DataTemplate> 
</ItemsControl.ItemTemplate> 

ItemsControl.ItemsPanelがそれらContentPresenter Sに包まこれらPianoKeyView Sは、その後に配置されたGridを生成する責任があります。

Iは、各列/行のサイズはまた、私はこのケースでそう特性(に渡すことができるように代わりに適宜 IEnumerable<ColumnDefinition>/ IEnumerable<RowDefinition>を取る私のオリジナル ColumnCountRowCount取り付け特性を変更した
<ItemsControl.ItemsPanel> 
    <ItemsPanelTemplate> 
     <Grid wpfBindingExtensions:GridExtensions.ColumnDefinitions="{Binding Keys, Converter={StaticResource keysToColumnDefinitionsConverter}}" 
       wpfBindingExtensions:GridExtensions.RowDefinitions="{Binding Keys, Converter={StaticResource keysToRowDefinitionsConverter}}"/> 
    </ItemsPanelTemplate> 
</ItemsControl.ItemsPanel> 

すべてのPianoKeyViewModelsを取り込み、それぞれの適切な星のサイジングでColumnDefinition/RowDefinitionを返すコンバーターを使用します。RowDefinitionsの割り当ては、自然なキーまたはシャープなキーのみがキーボードで必要とされるエッジのケース(たとえば、BからC、EからF、または単一のシャープまたはナチュラルキー)の場合にのみ割り当てられます。添付プロパティをRowDefinitionsためのコードは、基本的に(のみRowDefinitionsの代わりColumnDefinitionsでの作業)ColumnDefinitionsプロパティと同じロジックですので、私はちょうどここColumnDefinitionsのための1つを投稿します:添付の詳細について

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); 
    } 
} 

をプロパティについては、「グリッドをItemsControlのパネルとして使用する」(http://blog.scottlogic.com/2010/11/15/using-a-grid-as-the-panel-for-an-itemscontrol.html)を参照してください。ここで、上記の静的クラスを取得した元のコードが見つかりました。そうでない場合は、ここで重要な要素は次のとおりです。

  1. ItemsControl.ItemContainerStyleContentPresenterスタイルに(例えばGrid.ColumnPanel.ZOrderなど)に取り付けパネルのプロパティを割り当てます。
  2. ItemsControl.ItemsPanelItemsPanelTemplateGridに設定し、そこからグリッドのColumnDefinitionsRowDefinitionsを設定します。

この回答は既にかなり長くなっていますが、誰かが投稿に関連すると感じたら教えてもらえません。そうでなければ、ここでの最終的な結果は...

PianoKeyboardView

関連する問題