繁体   English   中英

在用作模板时在属性中捕获 WPF 用户控件的数据

[英]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 应用程序。 我该如何处理这个问题?

这根本不是你要做的。 “正确的方式”有点奇怪,但一旦你掌握了它,它实际上就不那么烦人了。

其中一些,它可能已经在做。 如果您需要有关任何特定点的更多详细信息,请告诉我。

  1. 您需要一个实现INotifyPropertyChanged的主视图模型。
  2. MainViewModel公开了一个公共属性ObservableCollection<NotificationObject> Notes { get {...} set {...} } ,它引发了set PropertyChanged

  3. MainViewModel负责填充Notes 它永远不应该知道或关心谁在看Notes 这超出了它的职责范围。

  4. 使用您的视图模型:

     public MainWindow() { InitializeComponent(); DataContext = new MainViewModel(); }
  5. 将视图模型的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>
  6. NotificationObject应该实现INotifyPropertyChanged

  7. 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状态时刷新过滤器。

  8. 在视图中,没有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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM