[英]Capturing a WPF user control's data in a property when used as a template
我的研究遇到了麻烦。 我对 WPF 及其任何实践都比较陌生,也许我正在尝试一些我不应该做的事情......无论如何,我找不到解决我的问题的方法,所以我会问。
我创建了一个简单的 WPF 应用程序来充当通知框。 显示的窗口有一个包含这些通知的ListBox
。 我决定使用user-control
来模板化这些通知,并为每个通知添加所需的功能。 例如,完成、承认或忽略的能力。
我已经能够让这些通知显示得很好,但是我的问题是创建一个适当的绑定来访问整个自定义NotificationObject
(因为它是处理该逻辑的应用程序,而不是用户控件本身)。
我所拥有的如下。
该窗口有一个ListBox
定义如下:
<ListBox Name="NotificaitonContainer" ItemTemplate="{StaticResource NotificationTemplate}"/>
ItemSource
在后端设置为: NotificaitonContainer.ItemsSource = notificationList;
模板定义为:
<DataTemplate x:Key="NotificationTemplate">
<StackPanel Orientation="Horizontal" Margin="5,5,5,5">
<uc:Notification CompleteClicked="CompleteTask" />
</StackPanel>
</DataTemplate>
user-control
定义为:
<UserControl x:Class="Notification_WPF.Windows.Components.Notification"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Height="50" Width="400" Background="#FFACA4A4">
<Grid>
<Label Content="{Binding Path=name}" />
<Button Name="completeBtn" Content="complete" Click="completeBtn_Click"/>
</Grid>
后面的代码定义为:
public partial class Notification : UserControl{
NotificationObject note;
public event EventHandler CompleteClicked;
public Notification() {
InitializeComponent();
}
private void completeBtn_Click(object sender, RoutedEventArgs e) {
((Panel)this.Parent).Children.Remove(this);
if (CompleteClicked != null) {
CompleteClicked(this, EventArgs.Empty);
}
}
}
现在,我遇到的问题是在使用这种绑定模式时设置note
对象。 我希望通过我的CompleteClicked
事件处理程序传递note
对象,以便我可以正确引用正在完成的通知。 但是我不知道如何在创建NotificationObject
时捕获NotificationObject
。
有谁知道如何捕获被绑定的数据对象并将其存储在参数上?
编辑
由于我个人的困惑,我添加此部分是为了明确我的意图。 我想要完成的是一个桌面托盘应用程序,它定期从自定义任务管理 Web 应用程序中提取,并在有限的时间范围内通知用户分配给他们的给定任务的状态。
我的应用程序的托盘应用程序部分运行良好,但是在其中我创建了一个在计时器上运行的事件,该事件处理为:
private void TimerHandeler(object sender, EventArgs e) {
List<NotificationObject> NotifyList = getNotificationList();
if (notificationBox.Visibility == Visibility.Visible) {
notificationBox.Display(NotifyList);
} else {
notificationBox.Dispatcher.Invoke(DispatcherPriority.Render, null);
}
}
notificationBox
ListBox
本质上是我的MainWindow
,包含我正在使用的ListBox
,如上所示。
为了显示它,我的代码如下:
public void Display(List<NotificationObject> notifications) {
var desktop = SystemParameters.WorkArea;
this.Left = desktop.Right - this.Width - 15;
this.Top = desktop.Bottom - this.Height - 15;
this.Focus();
buildTaskList(notifications);
this.Show();
}
private void buildTaskList(List<NotificationObject> notifications) {
//this is how I did this before... which is apparently incorrect.
this.notificationList = notifications;
NotificaitonContainer.ItemsSource = this.notificationList;
}
我被告知的新NotificatonList
应该是MainViewModel
并且看起来像:
class NotificationList : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<NotificationObject> _noteList;
public ObservableCollection<NotificationObject> NoteList {
get { return _noteList; }
set {
_noteList = value;
onListChange();
}
}
private void onListChange() {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(null));
}
}
}
我希望我把它放在一起......
现在我想做的是单击notificationBox 列表框中的notificationBox
并更新Web 应用程序。 我该如何处理这个问题?
这根本不是你要做的。 “正确的方式”有点奇怪,但一旦你掌握了它,它实际上就不那么烦人了。
其中一些,它可能已经在做。 如果您需要有关任何特定点的更多详细信息,请告诉我。
INotifyPropertyChanged
的主视图模型。 MainViewModel
公开了一个公共属性ObservableCollection<NotificationObject> Notes { get {...} set {...} }
,它引发了set
PropertyChanged
。
MainViewModel
负责填充Notes
。 它永远不应该知道或关心谁在看Notes
。 这超出了它的职责范围。
使用您的视图模型:
public MainWindow() { InitializeComponent(); DataContext = new MainViewModel(); }
将视图模型的Notes
属性绑定到主视图 XAML 中的ListBox.ItemsSource
。 无需以编程方式进行。 更好,事实上,如果你不这样做。 现在,如果您为Notes
分配一个新集合,它将侦听PropertyChanged
并更新列表框。 MVVM 的主题是您在视图模型上设置属性,然后在 UI 中神奇地(我告诉你,神奇地)发生了事情。
<ListBox ItemsSource="{Binding Notes}" > <ListBox.ItemTemplate> <DataTemplate> <!-- Notification no longer has the CompleteClicked event. Notification.DataContext will be the NotificationObject for this ListBox item. You're already using that fact to bind the name property. --> <uc:Notification /> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
NotificationObject
应该实现INotifyPropertyChanged
。
NotificationObject
获取一个IsCompleted
属性,并在IsCompleted
变为 true 时引发Completed
事件。 或者,您可以有一个IsCompletedChanged
事件,当IsCompleted
从 false 变为 true 时引发,反之亦然。
private bool _isCompleted = false; public bool IsCompleted { get { return _isCompleted; } set { if (_isCompleted != value) { _isCompleted = value; if (_isCompleted) OnCompleted(); OnPropertyChanged(); } } } // Purists may argue that an event handler's "args" parameter should always // inherit from EventArgs, and they may be right, but it's no capital crime. public event EventHandler<NotificationObject> Completed; private void OnCompleted() { if (Completed != null) { Completed(this, this); } }
MainViewModel
的处理程序:
void note_Completed(object sender, NotificationObject note) { Notes.Remove(note); }
它在创建NotificationObject
实例并用它们填充Notes
进行设置。 相反,您可能会丢失此事件,并且MainViewModel
也可以只处理它创建的NotificationObject
上的PropertyChanged
。 这是一个品味问题; 我更喜欢这种方式。
或者,根据您的要求,简单地通过IsCompleted
过滤 ListBox 中显示的项目可能会更好。 然后MainViewModel
将在任何NotificationObject
更改其IsCompleted
状态时刷新过滤器。
在视图中,没有CompletedClicked
事件和私有note
属性。 相反,它只是告诉它的Note
它是完整的。 这样做的后果与视图无关。 这就是视图模型之间的全部内容。
private void completeBtn_Click(object sender, RoutedEventArgs e) { ((NotificationObject)DataContext).IsCompleted = true; }
或者跳过点击事件并使用切换按钮:
<ToggleButton IsChecked="{Binding IsCompleted}" Content="Complete" />
当用户单击它时,它会将IsCompleted
设置为 true。 完毕。 当你发现你的代码隐藏缩小到只有一个构造函数时,或者当你发现你的用户控件可能只是一个没有代码的普通DataTemplate
时,这表明你在做 MVVM 是正确的。
您可能正在阅读本文,并且您的眼睛在逆时针旋转。 如果是这样,那就可以理解了。 事情是这样的:一旦你习惯了它实际上并没有那么糟糕,它会让你处于一个位置,你只需设置一堆东西,以声明方式将它们链接在一起,并且在大多数情况下它只是有效。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.