2012-08-08 115 views
6

也许你们可以帮我弄清楚这一点:我有一个字典和一个绑定到该字典的ItemsControl。每个条目的关键字确定ItemsControl中每个条目的内容,该值确定每个条目的宽度。这是一个大问题:宽度是一个百分比值,所以它告诉我,例如,我的项目需要是其父项大小的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改变大小,项目也会改变。一个网格是不需要的。此解决方案仅创建相对宽度,但当然可以将相同的解决方案应用于项目的高度。这是一个短版,为更全面的解释见楼下:

XAML

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

转换

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 2012-08-08 13:03:45

+0

你是什么意思?“我必须在网格开始时定义GridDefinition?”你可以在'DataTemplate'中定义你的网格 - 它不需要在'ItemsPanelTemplate'中。 – 2012-08-08 13:03:53

+0

嗨。看到我的编辑,20%应该是ItemControls父级(或标签父级)的20%。因为我可以使标签父级拉伸在两个方向上都是相同的。至于网格的开始:是的,我可以在DataTemplate中定义我的网格,但每个项目都有自己的网格,这是行不通的,还是我在这里监督某些东西? – BlackWolf 2012-08-08 13:12:10

回答

7

之间的空间可以实现IValueConverter

UPDATE

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

MultiBinding强制更新绑定目标,当ActualWidth更改时。

+0

这听起来很有希望,但似乎并不奏效。当调用Converter时,ItemsControl的ActualWidth为0,因为它尚未绘制。尽管如此,似乎对我而言是一个好的方向。 – BlackWolf 2012-08-08 13:39:29

+1

@BlackWolf,更新了答案。 – Dennis 2012-08-08 13:58:29

+0

非常感谢你,Multibinding像一个魅力!当ActualWidth改变时,我尝试手动更新绑定,但它不起作用。与多重绑定它工作正常。清洁和好的解决方案! – BlackWolf 2012-08-08 14:07:21

0

您可以尝试将Grid放入ItemTemplate。这Grid将有2列:1为实际的内容和一个为空的空间。您应该能够使用您的字典的Value绑定这些列的宽度,可能需要借助IValueConverter

如果内容需要为中心,你需要创建3列,分裂列0和2

+0

不要绑定宽度,请在ColumnDefinition上使用SharedSizeGroup属性。 – jtimperley 2012-08-08 13:07:44

+0

我认为这对我没有任何帮助,请参阅我的编辑。我需要每个项目都是一列,并且所有项目彼此相邻排列,因此不幸的是我无法使用空格。 – BlackWolf 2012-08-08 13:54:06

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