gpt4 book ai didi

c# - 公开 ViewModel 事件以绑定(bind)到自定义 DependencyProperty

转载 作者:太空宇宙 更新时间:2023-11-03 21:29:47 25 4
gpt4 key购买 nike

是否有可能从我的 ViewModel 公开一个公共(public)事件,以便允许它绑定(bind)到我的 View 中的自定义 DependencyProperty?

我的应用程序是使用 .NET 4.5 框架用 C# 编写的。它具有 MVVM 架构,在 View 中没有代码隐藏和自定义 DependencyProperty 类,用于将 View 的 WPF 特定行为绑定(bind)到 ViewModel 公开的属性。

我希望 ViewModel 能够公开一组属性,这些属性表示 View 需要响应的事件。例如,当顶级 ViewModel 对象即将被处置时,我希望 WPF View 实现通过关闭相应的 Window 来响应。当配置过程显示对话窗口、用户输入并确认信息并且 ViewModel 已将其传递给模型并且不再需要时,可能会发生这种情况。

我知道有很多问题专门针对解决“ViewModel 的显示对话框”问题;这不是其中之一,我有解决方案。

我已经通读了 DependencyProperties 的 MSDN 文档,但找不到任何特定于绑定(bind)到事件属性的内容。

我想实现的是类似于下面的代码。此代码生成,但在显示 MainWindow 时导致典型的 System.Windows.Data Error: 40 : BindingExpression path error: 'RequestCloseEvent' property not found 错误。

我知道有很多问题与“请帮助我调试我的 System.Windows.Data 错误:40 问题”有关;这(可能)也不是其中之一。(但如果真的就是这样,我会很高兴。)

WindowBindableProperties.cs 中自定义 DependencyProperty 的来源:

using System;
using System.Threading;
using System.Windows;

namespace WpfEventBinding
{
public static class WindowBindableProperties
{
#region ViewModelTerminatingEventProperty

/// <summary>
/// Register the ViewModelTerminatingEvent custom DependencyProperty.
/// </summary>
private static DependencyProperty _viewModelTerminatingEventProperty =
DependencyProperty.RegisterAttached
(
"ViewModelTerminatingEvent",
typeof(ViewModelTerminatingEventHandler),
typeof(WindowBindableProperties),
new PropertyMetadata(null, ViewModelTerminatingEventPropertyChanged)
);

/// <summary>
/// Identifies the ViewModelTerminatingEvent dependency property.
/// </summary>
public static DependencyProperty ViewModelTerminatingEventProperty
{ get { return _viewModelTerminatingEventProperty; } }

/// <summary>
/// Gets the attached ViewModelTerminatingEvent dependecy property.
/// </summary>
/// <param name="dependencyObject">The window attached to the WindowViewModel.</param>
/// <returns>The ViewModelTerminatingEventHandler bound to this property</returns>
public static ViewModelTerminatingEventHandler GetViewModelTerminatingEvent
(DependencyObject dependencyObject)
{
return (dependencyObject.GetValue(ViewModelTerminatingEventProperty)
as ViewModelTerminatingEventHandler);
}

/// <summary>
/// Sets the ViewModelTerminatingEvent dependency property.
/// </summary>
public static void SetViewModelTerminatingEvent(
DependencyObject dependencyObject,
ViewModelTerminatingEventHandler value)
{
dependencyObject.SetValue(ViewModelTerminatingEventProperty, value);
}

/// <summary>
/// Gets the ViewModelTerminatingEvent dependency property.
/// </summary>
private static void ViewModelTerminatingEventPropertyChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
Window instance = d as Window;
if (null != instance)
{
if (null != e.OldValue)
{
throw new System.InvalidOperationException(
"ViewModelTerminatingEvent dependency property cannot be changed.");
}

if (null != e.NewValue)
{
// Attach the Window.Close() method to the ViewModel's event
var newEvent = (e.NewValue as ViewModelTerminatingEventHandler);
newEvent += new ViewModelTerminatingEventHandler(() => instance.Close());
}
}
}

#endregion
}
}

MainWindow.xaml 的来源:(此示例包含用于简化停止按钮实现的代码隐藏。)

<Window x:Class="WpfEventBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:v="clr-namespace:WpfEventBinding"
v:WindowBindableProperties.ViewModelTerminatingEvent="{Binding Path=RequestCloseEvent}"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="{Binding Path=CloseCommandName}" Click="StopButton_Click" ></Button>
</Grid>
</Window>

MainWindow.xaml.cs 的来源(代码隐藏):

using System.Windows;

namespace WpfEventBinding
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}

private void StopButton_Click(object sender, RoutedEventArgs e)
{
MainWindowViewModel vm = (DataContext as MainWindowViewModel);
if (null != vm)
{
vm.Stop();
}
}
}
}

MainWindowViewModel.cs 的来源:

using System;
using System.ComponentModel;

namespace WpfEventBinding
{
public delegate void ViewModelTerminatingEventHandler();

class MainWindowViewModel
: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;

// Raised by the ViewModel to indicate to the view that it is no longer required.
// Causes System.Windows.Data Error: 40 : BindingExpression path error. Is it
// Possible to bind to an 'event' property?
public event ViewModelTerminatingEventHandler RequestCloseEvent;

// This has to have the public 'get' to allow binding. Is there some way to
// do the same thing for the 'event'?
public String CloseCommandName { get; private set; }

public MainWindowViewModel()
{
CloseCommandName = "Close";
}

internal void Stop()
{
ViewModelTerminatingEventHandler RaiseRequestCloseEvent =
RequestCloseEvent;
if (null != RaiseRequestCloseEvent)
{
RaiseRequestCloseEvent();
}
}

internal void Start()
{
OnPropertyChanged("CloseCommandName");
OnPropertyChanged("ViewModelTerminatingEvent");
}

private void OnPropertyChanged(String propertyName)
{
PropertyChangedEventHandler RaisePropertyChangedEvent = PropertyChanged;
if (RaisePropertyChangedEvent != null)
{
var propertyChangedEventArgs = new PropertyChangedEventArgs(propertyName);
RaisePropertyChangedEvent(this, propertyChangedEventArgs);
}
}
}
}

App.xaml 的来源:

<Application x:Class="WpfEventBinding.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Application.Resources>
<!-- Nothing to see here. Move along... -->
</Application.Resources>
</Application>

App.xaml.cs 的来源

using System.Windows;

namespace WpfEventBinding
{
public partial class App : Application
{
public App()
{
Startup += new StartupEventHandler(App_Startup);
}

void App_Startup(object sender, StartupEventArgs e)
{
MainWindowViewModel vm = new MainWindowViewModel();
MainWindow window = new MainWindow();

// Make sure this is set before attempting binding!
window.DataContext = vm;
vm.Start();
window.Show();
}
}
}

似乎 public event ViewModelTerminatingEventHandler RequestCloseEvent; 语法不足以允许数据绑定(bind)发生。类似的问题是查看 public String CloseCommandName { get;私有(private)集; 被声明为 public String CloseCommandName; 而没有 { get;私有(private)集; }。但是,没有 { get;私有(private)集; } 事件,它使用 {add{} remove{}} 语法(这也不能解决问题)。

我正在尝试的是否可行?如果可行,我错过了什么?

最佳答案

View closing 表示窗口关闭事件。所以你基本上想要对 View 中的事件使用react。我最近读了这个 arcticle , 有一个很好的图像

enter image description here

并且还提到了EventBehavior的存在。

如果您不想隐藏任何代码,最好的办法是使用行为。行为是一个简单的附加属性,它可以执行操作,例如上升的应用程序范围的命令,然后 ViewModel 可以捕获这些操作而不会出现 MVVM 问题。

这是一个行为示例:

public static class FreezeBehavior
{
public static bool GetIsFrozen(DependencyObject obj)
{
return (bool)obj.GetValue(IsFrozenProperty);
}
public static void SetIsFrozen(DependencyObject obj, bool value)
{
obj.SetValue(IsFrozenProperty, value);
}
public static readonly DependencyProperty IsFrozenProperty =
DependencyProperty.RegisterAttached("IsFrozen", typeof(bool), typeof(FreezeBehavior), new PropertyMetadata(OnIsFrozenChanged));

private static void OnIsFrozenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
{
var freezable = d as Freezable;
if (freezable != null && freezable.CanFreeze)
freezable.Freeze();
}
}
}

是这样使用的

<DropShadowEffect ShadowDepth="2" local:FreezeBehavior.IsFrozen="True"/>

它可以附加到任何 freezable 上以卡住它。在您的情况下,您想订阅事件并调用命令或设置属性,或任何通知 ViewModel 的内容。

关于c# - 公开 ViewModel 事件以绑定(bind)到自定义 DependencyProperty,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24936700/

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