2017-11-03 24 views
-2

확장 가능한 피아노 키보드를 만들기 위해 첨부 된 속성을 사용하여 ItemsControl로 WPF Grid를 사용하려고합니다. 키보드의 각 키는 앞뒤의 내용에 따라 1에서 3 열까지 확장 될 수 있으며 선명한 경우 1 행, 자연의 경우 2 행을 차지합니다. Grid의 Column Count와 Row Count를 동적으로 설정할 수있는 2 개의 속성이 이미 있습니다. (각 열/행의 너비/높이 설정을 지원하기 위해 조정해야하지만).ItemTemplate 및 ItemsSource에 대한 부착 가능한 속성을 구현하는 방법

지금 구현해야 할 것은 ItemsSource (Keys)과 ItemTemplate (PianoKeyView)의 두 가지 부착 가능 속성입니다. ItemsControlItemsPanel에 대해서만 UniformGrid을 그리드로 지원하며 특정 항목/행에 특정 항목을 지정하지 않으므로 Grid 컨트롤에서 사용해야합니다. 내 피아노 키보드는 옥타브 당 17 개의 열을 필요로하지만 ItemsControl은 12 개의 키만 전달되므로 UniformGrid에 12 개의 열만 만듭니다. 필자는 각 필수 열의 색인이 포함 된 1 옥타브 피아노 키보드의 이미지를 포함 시켰습니다. 그것이 현재 내가 GridExtensions.ItemsSourceGridExtensions.ItemTemplate에 대한 구현을 잃었 약자로

PianoKeyboard Grid Column Indices

이 키보드에 대한 내 코드입니다. GridExtensions은 부착 가능한 속성을 포함하는 정적 클래스입니다.

그리고이 GridExtensions의 ItemTemplate을 부착 속성에 대한 ItemTemplateChanged 처리기에 대한 코드는

<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> 

은 컴파일되지 않는 선 위의 두 수행 할 작업을 확인합니다. 내가 직접 GridItemsPanel와 함께 ItemsControl을 달성 할 수있는 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 변환기 및 4 DP 부착? 더 복잡한보기 대신 복잡한보기를 사용하면 ItemsControl의 바인딩에 더 편리한 양식을 데이터에 갖도록보기 모델을 단순화해야합니다. – ASh

+0

다른 옵션은 ItemsPanelTemplate에서 가로 StackPanel을 사용하고 음수 여백 및 Panel.ZIndex로 재생하는 것입니다. 스타일/바인딩을 통해/인접하지 않은 흰색 키를 확장하고 검정 키의 "아래"를 만나는 것은 무엇이든합니다. –

+0

@Ed Plunkett @ASS ItemsControl을 사용하고 싶지만 12 개 항목에 대해 17 개의 열을 생성하려면 어떻게해야합니까? 나는 원래 이것을'ItemsControl'으로 가지고 있었지만'PianoKey'의'Grid.Column'과'Grid.Row' 속성은'UniformGrid'에서 작동하지 않았습니다. 그리고'Grid'가 'ItemsPanelTemplate'은 컬럼을 생성하지 않았습니다. – Sphynx

답변

0

.

누락 된 부분은 Style이고 TargetTypeContentPresenter입니다. 이 스타일에서는 Grid.RowSpan, Grid.ColumnGrid.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.ItemsPanelContentPresenter들에 싸여이 PianoKeyView의는 다음에 배치됩니다있는 Grid를 생성하는 책임이있다. 각 열의 크기/행이 또한 내가이 경우에는 그렇게 할 속성 (에 전달 될 수 있도록

<ItemsControl.ItemsPanel> 
    <ItemsPanelTemplate> 
     <Grid wpfBindingExtensions:GridExtensions.ColumnDefinitions="{Binding Keys, Converter={StaticResource keysToColumnDefinitionsConverter}}" 
       wpfBindingExtensions:GridExtensions.RowDefinitions="{Binding Keys, Converter={StaticResource keysToRowDefinitionsConverter}}"/> 
    </ItemsPanelTemplate> 
</ItemsControl.ItemsPanel> 

내가 대신 IEnumerable<ColumnDefinition>/IEnumerable<RowDefinition> 적절한 취할 내 원래 ColumnCountRowCount 부착 특성을 수정 한 변환기를 통해 모든 PianoKeyViewModels을 가져 와서 적절한 별 크기 조정을 사용하여 ColumnDefinition/RowDefinition을 반환합니다.RowDefinitions에 대한 할당은 키보드에서 자연스러운 키나 예리한 키만 필요로하는 가장자리의 경우에만 적용됩니다 (예 : B에서 C, E에서 F 또는 단일 또는 자연스러운 키). 부동산 연결된 RowDefinitions의 코드는 기본적으로 (만 대신 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); 
    } 
} 

을 속성, "ItemsControl에 대한 패널로 그리드 사용"(http://blog.scottlogic.com/2010/11/15/using-a-grid-as-the-panel-for-an-itemscontrol.html)을 참조하십시오. 여기서 위의 정적 클래스를 파생시킨 원래 코드를 발견했습니다. 그렇지 않으면, 여기에 핵심 요소는 다음과 같습니다 ItemsControl.ItemContainerStyle에서 ContentPresenter 스타일 (예 : Grid.ColumnPanel.ZOrder 등)

  1. 지정 부착 패널의 속성.
  2. ItemsControl.ItemsPanel을 으로 설정하고 그리드의 ColumnDefinitionsRowDefinitions을 거기에서 설정하십시오.

이 답변은 이미 상당히 길어졌지만 다른 사람이 게시물에 관련 있다고 생각하면 알려주므로 모든 변환기를 게시하지 않았습니다. 그렇지 않으면, 여기서 최종 결과였다 ...

PianoKeyboardView