我在ItemsControl中有一个Canvas设置,我用按钮填充它。每个按钮都有一个X和Y位置属性,用于在画布上定位按钮。
我想要做的是使项目位置相对于画布的大小。我的问题是当我调整画布大小时,按钮ViewModels没有得到NotifyPropertyChanged调用,因此它们的位置永远不会更新。
<ItemsControl ItemsSource="{Binding Buttons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Top" Value="{Binding PosY, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Setter Property="Canvas.Left" Value="{Binding PosX, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:ToggleButton}">
<local:ToggleButton DataContext="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
ToggleButtonViewModel中的:
public double PosY {
get {
return (_Button.PositionVertical * Size[1]);
}
}
public double PosX {
get { return (_Button.PositionHorizontal * Size[0]); }
}
尺寸是双倍[],具有画布的X和Y尺寸。我已经尝试将它存储在CanvasViewModel中,在这种情况下我可以在画布上使用SizeChanged事件更新它,但是当我改变尺寸时,我无法弄清楚如何更新ToggleButtonViewModel中的PosX和PosY。 我也尝试将大小存储为所有ToggleButtonViewModel中的静态参数,但我不能使用带有静态参数的NotifyPropertyChanged。
任何想法如何在画布大小改变时如何更新PosX和PosY?
答案 0 :(得分:0)
我认为进入这里的方法是实现一个自定义面板,它可以覆盖你的编排逻辑,就像Clemens建议的那样,并为定位添加一些附加的属性。 我为它做了一个非常快速的小解决方案,它似乎像你想要的那样工作。
它是一个简单的Panel&#34; RelativePositionCanvas &#34;,提供两个附加的属性&#34; RelativeX &#34;和&#34; RelativeY &#34;它们是双精度值,通常在0到1之间,用于设置左上角相对于面板大小的位置。
如果要添加更多内容,例如将其对齐到右侧,或者使大小相对,则只需为这些添加一些附加属性,并在ArrangeOverride方法中使用它们。
public class RelativePositionCanvas : Panel
{
#region Properties
#region RelativeX
public static readonly DependencyProperty RelativeXProperty =
DependencyProperty.RegisterAttached(
"RelativeX",
typeof(double),
typeof(RelativePositionCanvas),
new FrameworkPropertyMetadata(
0.0d,
new PropertyChangedCallback(OnPositioningChanged)));
public static double GetRelativeX(DependencyObject d)
{
return (double)d.GetValue(RelativeXProperty);
}
public static void SetRelativeX(DependencyObject d, double value)
{
d.SetValue(RelativeXProperty, value);
}
#endregion
#region RelativeY
public static readonly DependencyProperty RelativeYProperty =
DependencyProperty.RegisterAttached(
"RelativeY",
typeof(double),
typeof(RelativePositionCanvas),
new FrameworkPropertyMetadata(
0.0d,
new PropertyChangedCallback(OnPositioningChanged)));
public static double GetRelativeY(DependencyObject d)
{
return (double)d.GetValue(RelativeYProperty);
}
public static void SetRelativeY(DependencyObject d, double value)
{
d.SetValue(RelativeYProperty, value);
}
#endregion
private static void OnPositioningChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uie = d as UIElement;
if (uie != null)
{
RelativePositionCanvas p = VisualTreeHelper.GetParent(uie) as RelativePositionCanvas;
if (p != null)
p.InvalidateArrange();
}
}
#endregion
protected override Size MeasureOverride(Size availableSize)
{
Size childConstraint = new Size(Double.PositiveInfinity, Double.PositiveInfinity);
foreach (UIElement child in InternalChildren)
{
if (child == null) { continue; }
child.Measure(childConstraint);
}
return new Size();
}
protected override Size ArrangeOverride(Size arrangeSize)
{
var children = this.Children;
foreach(UIElement child in children)
{
if (child == null) { continue; }
var childRelativeX = GetRelativeX(child);
var childRelativeY = GetRelativeY(child);
var posX = childRelativeX * arrangeSize.Width;
var posY = childRelativeY * arrangeSize.Height;
child.Arrange(new Rect(new Point(posX, posY), child.DesiredSize));
}
return arrangeSize;
}
}
要使用它,你必须用RelativePositionCanvas替换ItemsPanelTemplate,用 RelativePositionCanvas.RelativeY替换 Canvas.Top 和 Canvas.Left 属性。 和 RelativePositionCanvas.RelativeX 属性,应该没问题
答案 1 :(得分:0)
选项1)在ToggleButtonViewModel中,将PosX和PosY的定义更改为“正常”WPF通知属性(我假设您知道我的意思)更改后通知。当“大小”中的值更改时,请在ViewModel上设置属性。例如:
_viewModel.PosY = _Button.PositionVertical * Size[1];
_viewModel.PosX = _Button.PositionHorizontal * Size[0];
这可以很好地分离关注点;但可能无法实现,具体取决于您持有哪些信息。
选项2)向ViewModel添加一个方法,您可以调用该方法来指示大小已更改,您可以在其中:
RaiseNotifyPropertyChanged(nameof(PosX));
RaiseNotifyPropertyChanged(nameof(PosY));
(如果需要,我可以为您提供RaiseNotifyPropertyChanged的实现,但它是您在ViewModel中可能已经拥有的非常标准的WPF样板代码。)