gpt4 book ai didi

wpf - 将只读 GUI 属性推回 ViewModel

转载 作者:行者123 更新时间:2023-12-03 04:33:16 28 4
gpt4 key购买 nike

我想编写一个 ViewModel,它始终知道 View 中某些只读依赖属性的当前状态。

具体来说,我的 GUI 包含一个 FlowDocumentPageViewer,它一次显示 FlowDocument 中的一页。 FlowDocumentPageViewer 公开两个名为 CanGoToPreviousPage 和 CanGoToNextPage 的只读依赖属性。我希望我的 ViewModel 始终知道这两个 View 属性的值。

我想我可以使用 OneWayToSource 数据绑定(bind)来做到这一点:

<FlowDocumentPageViewer
CanGoToNextPage="{Binding NextPageAvailable, Mode=OneWayToSource}" ...>

如果允许这样做,那就完美了:每当 FlowDocumentPageViewer 的 CanGoToNextPage 属性发生更改时,新值就会被推送到 ViewModel 的 NextPageAvailable 属性中,这正是我想要的。

不幸的是,这无法编译:我收到一条错误消息,指出'CanGoToPreviousPage'属性是只读的,无法从标记中设置。显然只读属性不支持任何类型的数据绑定(bind),甚至不是对该属性而言只读的数据绑定(bind)。

我可以将 ViewModel 的属性设置为 DependencyProperties,并以相反的方式进行 OneWay 绑定(bind),但我并不热衷于关注点分离违规(ViewModel 需要对 View 的引用,MVVM 数据绑定(bind)是应该避免)。

FlowDocumentPageViewer 不会公开 CanGoToNextPageChanged 事件,而且我不知道有什么好方法可以从 DependencyProperty 获取更改通知,除非创建另一个 DependencyProperty 来将其绑定(bind),这在这里似乎有些过分了。

如何让我的 ViewModel 了解 View 只读属性的更改?

最佳答案

是的,我过去曾使用 ActualWidth 完成过此操作和ActualHeight属性,这两个属性都是只读的。我创建了一个具有 ObservedWidth 的附加行为和ObservedHeight附加属性。它还具有 Observe用于进行初始连接的属性。用法如下:

<UserControl ...
SizeObserver.Observe="True"
SizeObserver.ObservedWidth="{Binding Width, Mode=OneWayToSource}"
SizeObserver.ObservedHeight="{Binding Height, Mode=OneWayToSource}"

所以 View 模型有 WidthHeight始终与 ObservedWidth 同步的属性和ObservedHeight附加属性。 Observe属性仅附加到 SizeChanged事件FrameworkElement 。在句柄中,它更新其 ObservedWidthObservedHeight特性。因此,WidthHeight View 模型的值始终与 ActualWidth 同步和ActualHeight UserControl的.

也许不是完美的解决方案(我同意 - 只读 DP应该 支持 OneWayToSource 绑定(bind)),但它有效并且支持 MVVM 模式。显然,ObservedWidthObservedHeight DP 不是只读的。

更新:这里是实现上述功能的代码:

public static class SizeObserver
{
public static readonly DependencyProperty ObserveProperty = DependencyProperty.RegisterAttached(
"Observe",
typeof(bool),
typeof(SizeObserver),
new FrameworkPropertyMetadata(OnObserveChanged));

public static readonly DependencyProperty ObservedWidthProperty = DependencyProperty.RegisterAttached(
"ObservedWidth",
typeof(double),
typeof(SizeObserver));

public static readonly DependencyProperty ObservedHeightProperty = DependencyProperty.RegisterAttached(
"ObservedHeight",
typeof(double),
typeof(SizeObserver));

public static bool GetObserve(FrameworkElement frameworkElement)
{
frameworkElement.AssertNotNull("frameworkElement");
return (bool)frameworkElement.GetValue(ObserveProperty);
}

public static void SetObserve(FrameworkElement frameworkElement, bool observe)
{
frameworkElement.AssertNotNull("frameworkElement");
frameworkElement.SetValue(ObserveProperty, observe);
}

public static double GetObservedWidth(FrameworkElement frameworkElement)
{
frameworkElement.AssertNotNull("frameworkElement");
return (double)frameworkElement.GetValue(ObservedWidthProperty);
}

public static void SetObservedWidth(FrameworkElement frameworkElement, double observedWidth)
{
frameworkElement.AssertNotNull("frameworkElement");
frameworkElement.SetValue(ObservedWidthProperty, observedWidth);
}

public static double GetObservedHeight(FrameworkElement frameworkElement)
{
frameworkElement.AssertNotNull("frameworkElement");
return (double)frameworkElement.GetValue(ObservedHeightProperty);
}

public static void SetObservedHeight(FrameworkElement frameworkElement, double observedHeight)
{
frameworkElement.AssertNotNull("frameworkElement");
frameworkElement.SetValue(ObservedHeightProperty, observedHeight);
}

private static void OnObserveChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var frameworkElement = (FrameworkElement)dependencyObject;

if ((bool)e.NewValue)
{
frameworkElement.SizeChanged += OnFrameworkElementSizeChanged;
UpdateObservedSizesForFrameworkElement(frameworkElement);
}
else
{
frameworkElement.SizeChanged -= OnFrameworkElementSizeChanged;
}
}

private static void OnFrameworkElementSizeChanged(object sender, SizeChangedEventArgs e)
{
UpdateObservedSizesForFrameworkElement((FrameworkElement)sender);
}

private static void UpdateObservedSizesForFrameworkElement(FrameworkElement frameworkElement)
{
// WPF 4.0 onwards
frameworkElement.SetCurrentValue(ObservedWidthProperty, frameworkElement.ActualWidth);
frameworkElement.SetCurrentValue(ObservedHeightProperty, frameworkElement.ActualHeight);

// WPF 3.5 and prior
////SetObservedWidth(frameworkElement, frameworkElement.ActualWidth);
////SetObservedHeight(frameworkElement, frameworkElement.ActualHeight);
}
}

关于wpf - 将只读 GUI 属性推回 ViewModel,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1083224/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com