簡體   English   中英

從其他ViewModel調用NotifyPropertyChanged

[英]Call NotifyPropertyChanged from different ViewModel

我在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]); }
    }

Size是帶有畫布的X和Y尺寸的double []。 我嘗試將其存儲在CanvasViewModel中,在這種情況下,可以使用畫布上的SizeChanged事件來對其進行更新,但是當尺寸更改時,我無法弄清楚如何在ToggleButtonViewModel中更新PosX和PosY。 我也嘗試將大小作為靜態參數存儲在所有ToggleButtonViewModels中,但是我無法將NotifyPropertyChanged與靜態參數一起使用。

有什么想法可以讓我在畫布大小更改時如何更新PosX和PosY?

我認為這里的方法是實現一個自定義面板,就像Clemens建議的那樣,該面板將覆蓋您的排列邏輯,並為定位添加一些附加的屬性。 我為此做了一個非常快速的小型解決方案,它看起來像您想要的那樣工作。

它是一個簡單的面板“ RelativePositionCanvas ”,提供了兩個附加的屬性“ RelativeX ”和“ RelativeY ”,它們是雙精度值,通常范圍從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.RelativeYRelativePositionCanvas.RelativeX屬性替換Canvas.TopCanvas.Left屬性,並且應該可以使用

選項1)在ToggleButtonViewModel中,將PosX和PosY的定義更改為“普通” WPF通知屬性(假定您知道我的意思),這些屬性將在更改時通知。 當Size中的值更改時,請在ViewModel上設置屬性。 例如:

_viewModel.PosY = _Button.PositionVertical * Size[1];
_viewModel.PosX = _Button.PositionHorizontal * Size[0];

這樣可以很好地分離關注點。 但可能無法實現,具體取決於您所持有的信息的位置。

Option2)在ViewModel中添加一個方法,您可以調用該方法以指示Size已更改,您可以在其中進行以下操作:

RaiseNotifyPropertyChanged(nameof(PosX));
RaiseNotifyPropertyChanged(nameof(PosY));

(如果需要,我可以為您提供RaiseNotifyPropertyChanged的實現,但這是ViewModel中可能已經包含的非常標准的WPF樣板代碼。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM