当用作模板时,在属性中捕获WPF用户控件'

本文关键字:WPF 用户 控件 属性 | 更新日期: 2023-09-27 18:12:31

我的研究遇到了一点麻烦。我对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

有谁知道如何捕获被绑定的数据对象并将其存储在参数上吗?

编辑

由于我个人的困惑,我添加这一节是为了使我的意图更加清晰。我想要完成的是一个桌面托盘应用程序,它定期从自定义任务管理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本质上是我的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列表框中的通知并更新web应用程序。我该怎么做呢?

当用作模板时,在属性中捕获WPF用户控件'

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

其中一些,它可能已经在做了。如果你需要任何具体的细节,请告诉我。
  1. 你需要一个实现INotifyPropertyChanged的主视图模型。
  2. MainViewModel公开了一个公共属性ObservableCollection<NotificationObject> Notes { get {...} set {...} },它在set . c中引发PropertyChanged

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

  4. 使用viewmodel:

    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事件。或者,您可以在IsCompleted从false变为true时触发IsCompletedChanged事件,反之亦然。

    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 s上处理PropertyChanged。这是个品位问题;我更喜欢这种方式。

    或者,根据您的需求,简单地通过IsCompleted过滤ListBox中显示的项目可能会更好。然后,当任何NotificationObject改变其IsCompleted状态时,MainViewModel将刷新过滤器。

  8. 在视图中,没有CompletedClicked事件,也没有私有note属性。相反,它只是告诉它的Note它已经完成了。这样做的后果与视图无关。这些都在视图模型之间。

    private void completeBtn_Click(object sender, RoutedEventArgs e) {
        ((NotificationObject)DataContext).IsCompleted = true;
    }
    

    或者跳过click事件并使用切换按钮:

    <ToggleButton IsChecked="{Binding IsCompleted}" Content="Complete" />
    

    当用户单击它时,它将IsCompleted设置为true。完成了。当你发现你的代码隐藏缩小到只有一个构造函数,或者当你发现你的usercontrol可能只是一个没有代码的普通DataTemplate,这是你做MVVM正确的标志。

你可能正在读这篇文章,你的眼睛正在逆时针旋转。如果是这样,那是可以理解的。事情是这样的:一旦你习惯了它,它实际上并没有那么糟糕,它把你放在一个地方,你只需要设置一堆东西,声明性地将它们链接在一起,并且在大多数情况下它只是工作