2012-08-08 3 views
6

어쩌면 내가 도와 줄 수 있습니다. 사전과 그 사전에 바인딩 된 ItemsControl이 있습니다. 각 입력의 Key는 ItemsControl의 각 Item의 내용을 결정하고 Value는 각 Item의 너비를 결정합니다. 큰 문제는 다음과 같습니다. 너비는 백분율 값이므로 예를 들어 내 항목이 상위 항목의 20 % 크기 여야합니다.WPF의 동적 백분율 기준 너비

어떻게하면됩니까? 그리드는 스타 기반 너비로 작업 할 수 있지만 그리드 시작 부분에 GridDefinition을 정의해야하므로 ItemsControl.ItemTemplate에서이를 수행 할 수 없습니다.

현재 코드 : 이것에

<ItemsControl ItemsSource="{Binding Distribution}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> 
     <ItemsControl.ItemsPanel> 
      <ItemsPanelTemplate> 
       <Grid IsItemsHost="True" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" /> 
      </ItemsPanelTemplate> 
     </ItemsControl.ItemsPanel> 
     <ItemsControl.ItemTemplate> 
      <DataTemplate> 
       <!-- I NEED THIS TO BE A CERTAIN PERCENTAGE IN WIDTH --> 
       <Label Content="{Binding Key.Text}" Foreground="{Binding Key.Color}"/> 
      </DataTemplate> 
     </ItemsControl.ItemTemplate> 
    </ItemsControl> 

어떤 아이디어? 이 문제를 해결할 수있는 우아한 방법이 있습니까?

감사합니다.

설명 : : 백분율은 ItemControls 부모를 기준으로합니다.

또 다른 하나 : 각 항목은 행이 아닌 표의 한 열로 간주됩니다. 그래서 모든 항목이 같은 줄에서 서로 옆에 있어야합니다.

솔루션 : 당신의 도움에 대한

덕분에,이 문제는 Multibinding을 사용하고 ItemsControl에의 ActualWidth 바인딩에 의해 해결 될 수있다. 이렇게하면 ItemsControl의 크기가 변경 될 때마다 Items도 변경됩니다. 그리드가 필요하지 않습니다. 이 솔루션은 상대 너비 만 생성하지만 동일한 솔루션이 물론 항목 높이에 적용될 수 있습니다.

<ItemsControl ItemsSource="{Binding Distribution}" Name="itemsControl" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> 
     <ItemsControl.ItemsPanel> 
      <ItemsPanelTemplate> 
       <StackPanel IsItemsHost="True" Orientation="Horizontal" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" /> 
      </ItemsPanelTemplate> 
     </ItemsControl.ItemsPanel> 
     <ItemsControl.ItemTemplate> 
      <DataTemplate> 
       <Label Content="{Binding Key.Text}" 
         Foreground="{Binding Key.Color}"> 
        <Label.Width> 
         <MultiBinding Converter="{StaticResource myConverter}"> 
          <Binding Path="Value"/> 
          <Binding Path="ActualWidth" ElementName="itemsControl"/> 
         </MultiBinding> 
        </Label.Width> 
       </Label> 
      </DataTemplate> 
     </ItemsControl.ItemTemplate> 
    </ItemsControl> 

변환기 :

XAML : 이것은 더욱 철저한 설명은 아래에 짧은 버전 다운 볼 수있다

class MyConverter : IMultiValueConverter 
{ 
    public object Convert(object[] value, Type targetType, object parameter, CultureInfo culture) 
    { 
     //[1] contains the ItemsControl.ActualWidth we binded to, [0] the percentage 
     //In this case, I assume the percentage is a double between 0 and 1 
     return (double)value[1] * (double)value[0]; 
    } 

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

을 그리고 그 트릭을 할해야합니다!

+0

어떤 부모의 20 %입니까? ItemsControl, Label 또는 다른 항목의 부모입니까? – jtimperley

+0

"그리드 시작 부분에 GridDefinition을 정의해야합니까?" 대신'DataTemplate'에서 그리드를 정의 할 수 있습니다 - 그것은 ItemsPanelTemplate에있을 필요가 없습니다. –

+0

안녕하세요. 내 편집을 참조하십시오. 20 %는 ItemControls 상위 (또는 상위 라벨)의 20 %라고 가정합니다. 레이블 양방향으로 부모를 늘일 수 있으므로 양방향으로 동일하게됩니다. 그리드 시작 : DataTemplate에서 그리드를 정의 할 수는 있지만, 각 아이템은 그리드가 작동하지 않을 것입니다. 그렇지 않으면 여기에서 무엇을 감독합니까? – BlackWolf

답변

7

사이의 빈 공간을 분할, 3 열을 만드는 데 필요한 것입니다.

업데이트.

MultiBinding 당신을 도울 것입니다.다음 예제는 다음과 같습니다

1) XAML :

<Window x:Class="WpfApplication1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:local="clr-namespace:WpfApplication1" 
     Title="MainWindow" Height="114" Width="404"> 
    <Grid> 
     <Grid.Resources> 
      <local:RelativeWidthConverter x:Key="RelativeWidthConverter"/> 
     </Grid.Resources> 

     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="*"/> 
     </Grid.ColumnDefinitions> 

     <ItemsControl ItemsSource="{Binding}" 
         x:Name="itemsControl"> 
      <ItemsControl.ItemTemplate> 
       <DataTemplate> 
        <Rectangle Fill="Green" Margin="5" Height="20" HorizontalAlignment="Left"> 
         <Rectangle.Width> 
          <MultiBinding Converter="{StaticResource RelativeWidthConverter}"> 
           <Binding Path="RelativeWidth"/> 
           <Binding Path="ActualWidth" ElementName="itemsControl"/> 
          </MultiBinding> 
         </Rectangle.Width> 
        </Rectangle> 
       </DataTemplate> 
      </ItemsControl.ItemTemplate> 
     </ItemsControl> 
    </Grid> 
</Window> 

2) 계산기 :

public class RelativeWidthConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     return ((Double)values[0] * (Double)values[1])/100.0; 
    } 

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

3)보기 모델 :

public class ViewModel : ViewModelBase 
{ 
    public ViewModel() 
    { 
    } 

    public Double RelativeWidth 
    { 
     get { return relativeWidth; } 
     set 
     { 
      if (relativeWidth != value) 
      { 
       relativeWidth = value; 
       OnPropertyChanged("RelativeWidth"); 
      } 
     } 
    } 
    private Double relativeWidth; 
} 

4) 코드 숨김 :

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
     DataContext = new[] 
     { 
      new ViewModel { RelativeWidth = 20 }, 
      new ViewModel { RelativeWidth = 40 }, 
      new ViewModel { RelativeWidth = 60 }, 
      new ViewModel { RelativeWidth = 100 }, 
     }; 
    } 
} 

MultiBindingActualWidth이 변경되면 바인딩 대상을 강제로 업데이트해야합니다.

+0

이것은 유망 스럽지만 작동하지 않는 것 같습니다. 변환기가 호출 될 때 ItemsControl의 ActualWidth는 아직 그려지지 않았으므로 0입니다. 그래도 나에게 좋은 방향 인 것 같아. – BlackWolf

+1

@BlackWolf, 답변을 업데이트했습니다. – Dennis

+0

대단히 감사합니다. 멀티 바인드는 매력처럼 작동했습니다! ActualWidth가 변경되었을 때 바인딩을 수동으로 업데이트하려고했지만 작동하지 않았습니다. multibinding 그것은 잘 작동합니다. 깨끗하고 좋은 솔루션! – BlackWolf

0

Grid을 넣으십시오. 이 Grid에는 2 개의 열이 있습니다. 실제 내용은 1이고 빈 공간은 1입니다. 사전의 Value을 사용하여 이러한 열의 너비를 바인드 할 수 있어야하며 가능하면 IValueConverter을 사용합니다. 콘텐츠를 중심으로해야하는 경우

, 당신은 당신이 IValueConverter을 구현할 수 있습니다 열 0과 2

+0

너비를 바인딩하지 마십시오. ColumnDefinition에서 SharedSizeGroup 속성을 사용하십시오. – jtimperley

+0

이것이 내게 도움이되지 않는다고 생각합니다. 편집을 참조하십시오. 나는 각각의 항목이 하나의 열이고 모든 항목이 서로 나란히 정렬되어야하므로 불행히도 공백을 사용할 수 없다. – BlackWolf

0

그리드를 사용할 수없는 사람이 필요했습니다.

나는 내용을 감싸서 동적 백분율 너비/높이를 추가 할 수있는 ContentControl을 만들었습니다.

/// <summary> 
/// This control has a dynamic/percentage width/height 
/// </summary> 
public class FluentPanel : ContentControl, IValueConverter 
{ 
    #region Dependencie Properties 

    public static readonly DependencyProperty WidthPercentageProperty = 
     DependencyProperty.Register("WidthPercentage", typeof(int), typeof(FluentPanel), new PropertyMetadata(-1, WidthPercentagePropertyChangedCallback)); 

    private static void WidthPercentagePropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) 
    { 
     ((FluentPanel)dependencyObject).OnWidthPercentageChange(); 
    } 

    public int WidthPercentage 
    { 
     get { return (int)GetValue(WidthPercentageProperty); } 
     set { SetValue(WidthPercentageProperty, value); } 
    } 

    public static readonly DependencyProperty HeightPercentageProperty = 
     DependencyProperty.Register("HeightPercentage", typeof(int), typeof(FluentPanel), new PropertyMetadata(-1, HeightPercentagePropertyChangedCallback)); 

    private static void HeightPercentagePropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) 
    { 
     ((FluentPanel)dependencyObject).OnHeightPercentageChanged(); 
    } 

    public int HeightPercentage 
    { 
     get { return (int)GetValue(HeightPercentageProperty); } 
     set { SetValue(HeightPercentageProperty, value); } 
    } 

    #endregion 

    #region Methods 

    private void OnWidthPercentageChange() 
    { 
     if (WidthPercentage == -1) 
     { 
      ClearValue(WidthProperty); 
     } 
     else 
     { 
      SetBinding(WidthProperty, new Binding("ActualWidth") { Source = Parent, Converter = this, ConverterParameter = true }); 
     } 
    } 

    private void OnHeightPercentageChanged() 
    { 
     if (HeightPercentage == -1) 
     { 
      ClearValue(HeightProperty); 
     } 
     else 
     { 
      SetBinding(HeightProperty, new Binding("ActualHeight") { Source = Parent, Converter = this, ConverterParameter = false }); 
     } 
    } 

    #endregion 

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     if ((bool)parameter) 
     { 
      // width 
      return (double)value * (WidthPercentage * .01); 
     } 
     else 
     { 
      // height 
      return (double)value * (HeightPercentage * .01); 
     } 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new NotSupportedException(); 
    } 
}