WPF 中基于百分比的动态宽度
Posted
技术标签:
【中文标题】WPF 中基于百分比的动态宽度【英文标题】:Dynamic percentage-based width in WPF 【发布时间】:2012-08-05 14:15:22 【问题描述】:也许你们可以帮我解决这个问题:我有一个字典和一个绑定到该字典的 ItemsControl。每个条目的 Key 决定了 ItemsControl 中每个 Item 的内容,而 Value 决定了每个 Item 的宽度。最大的问题是:宽度是一个百分比值,所以它告诉我,例如,我的 Item 的大小需要是其父项的 20%。
我怎样才能做到这一点? 我知道网格能够使用基于星的宽度,但由于我必须在网格的开头定义 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 父级!
还有一个:每个项目都应该是网格的一列,而不是一行。所以我需要所有项目在同一行中彼此相邻。
解决方案:
感谢您的帮助,这个问题可以通过使用多重绑定和绑定到 ItemsControl 的 ActualWidth 来解决。这样,每当 ItemsControl 的大小发生变化时,Items 也会发生变化。不需要网格。这个解决方案只创建一个相对宽度,但同样的解决方案当然可以应用于项目的高度。这是一个简短的版本,更详尽的解释见下文:
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();
这应该可以解决问题!
【问题讨论】:
20% 什么父母? ItemsControl、Label 或其他东西的父级? “我必须在网格的开头定义 GridDefinition”是什么意思?您可以在DataTemplate
中定义您的网格 - 它不需要在 ItemsPanelTemplate
中。
嗨。请参阅我的编辑,20% 应该是 ItemControls 父级(或标签父级)的 20%。因为我可以使 Labels 父级在两个方向上拉伸,所以它是相同的)。至于 Grid 的开头:是的,我可以在 DataTemplate 中定义我的 Grid,但是每个 Item 都会有自己的 Grid,这是行不通的,还是我在这里监督什么?
【参考方案1】:
我有一个不能使用网格的需求。
我创建了一个 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();
【讨论】:
【参考方案2】:你可以实现。IValueConverter
更新。
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
更改时强制更新绑定目标。
【讨论】:
这听起来很有希望,但似乎不起作用。调用 Converter 时,ItemsControl 的 ActualWidth 为 0,因为它尚未绘制。不过,这对我来说似乎是一个很好的方向。 非常感谢,多重绑定就像一个魅力!当 ActualWidth 更改时,我尝试手动更新绑定,但它不起作用。使用多重绑定它可以正常工作。干净又漂亮的解决方案!【参考方案3】:您可以尝试在ItemTemplate
中添加Grid
。这个Grid
将有 2 列:1 列用于实际内容,1 列用于空白空间。您应该能够使用字典的Value
绑定这些列的宽度,可能还可以借助IValueConverter
。
如果内容需要居中,您需要创建 3 列,将第 0 列和第 2 列之间的空白区域分开。
【讨论】:
不要绑定宽度,使用 ColumnDefinition 上的 SharedSizeGroup 属性。 我认为这对我没有帮助,请参阅我的编辑。我需要每个项目是一列,并且所有项目都需要彼此相邻对齐,因此很遗憾,我不能使用空格。以上是关于WPF 中基于百分比的动态宽度的主要内容,如果未能解决你的问题,请参考以下文章
我可以根据基于百分比的宽度设置 div 的高度吗? [复制]
当宽度根据百分比动态变化时,制作一个 <div> 正方形 [重复]