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