从PRISM开始学WPF(九)交互Interaction(弹窗)-更新至Prism7.1

0x07交互

[7.1updated]无变化
这是这个系列的最后一篇了,主要介绍了Prism中为我们提供几种弹窗交互的方式。

Notification通知式

Prism通过InteractionRequest 来实现弹窗交互,它是一个泛型接口,不同的类型对应不同类型的弹窗方式。
在使用InteractionRequest的时候需要在,xaml中需要注册一个Trigger:

    <i:Interaction.Triggers>
        <prism:InteractionRequestTrigger SourceObject="{Binding NotificationRequest}">
            <prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True" />
        </prism:InteractionRequestTrigger>
    </i:Interaction.Triggers>
Interaction

这里用到了Interaction,他是i命名空间里的东西,那么i是什么呢?
interactivity这个是微软内置的类库,他提供了一组用户交互的类,比如我们这里用到的EventTrigger可以用来执行事件触发的操作。
在使用的时候,先引入xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
或者xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity",然后在xaml中使用他:

<i:Interaction.Triggers>
    <i:EventTrigger>

    </i:EventTrigger>
</i:Interaction.Triggers>

而 prism:PopupWindowAction 的 IsModal=True意味着弹框不被关闭的时候,父窗体无法使用。我刚搜索了一下,这个词的翻译竟然“模态”。

模态对话框(Modal Dialogue Box,又叫做模式对话框),是指在用户想要对对话框以外的应用程序进行操作时,必须首先对该对话框进行响应。 如单击【确定】或【取消】按钮等将该对话框关闭。

好,接着,我们在code-behind中声明,使用INotification类型:

        public InteractionRequest<INotification> NotificationRequest { get; set; }

在command的回调函数中就可以使用NotificationRequest:

            NotificationRequest.Raise(new Notification { Content = "Notification Message", Title = "Notification" }, r => Title = "Notified");

最后通过ConfirmationRequest.Raise()方法来实现调用弹窗,这里将Title修改为“Notified”。

Confirmation 确认式

跟Notification的使用方法一样,先注册Trigger:

        <prism:InteractionRequestTrigger SourceObject="{Binding ConfirmationRequest}">
            <prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True" />
        </prism:InteractionRequestTrigger>

然后在使用InteractionRequest的时候使用IConfirmation类型:

        public InteractionRequest<IConfirmation> ConfirmationRequest { get; set; }

callback:

            ConfirmationRequest.Raise(new Confirmation {
                Title = "Confirmation",
                Content = "Confirmation Message" }, 
                r => Title = r.Confirmed ? "Confirmed" : "Not Confirmed");

原本一直好奇为什么r能获取confirmationconfirmed属性,后来才发现,自学这个东西,急于求成是不行的。
看下prism的 ConfirmationRequest.Raise()方法:

        /// <summary>
        /// Fires the Raised event.
        /// </summary>
        /// <param name="context">The context for the interaction request.</param>
        /// <param name="callback">The callback to execute when the interaction is completed.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
        public void Raise(T context, Action<T> callback)
        {
            var handler = this.Raised;
            if (handler != null)
            {
                handler(this, new InteractionRequestedEventArgs(context, () => { if(callback != null) callback(context); } ));
            }
        }
CustomPopupRequest 客制化

上面的通知和提示窗体,都是内置的,很多时候,我们需要自制一些弹窗来满足更复杂的使用场景,比如我们通过弹窗来传递一些信息,贴心的Prism同样为我们准备了一个接口IInteractionRequestAware:

        //
        // Summary:
        //     The Prism.Interactivity.InteractionRequest.INotification passed when the interaction
        //     request was raised.
        INotification Notification { get; set; }
        //
        // Summary:
        //     An System.Action that can be invoked to finish the interaction.
        Action FinishInteraction { get; set; }

蛤蛤,Notification正是我们需要的东西,再看看他是什么鬼

        //
        // Summary:
        //     Gets or sets the title to use for the notification.
        string Title { get; set; }
        //
        // Summary:
        //     Gets or sets the content of the notification.
        object Content { get; set; }

原来这个被用来传递的东西,也有标准,需要一个名字和一个内容,内容是object,也就是,我们可以用他来装下任何东西。

FinishInteractioninvoke来关闭交互界面,这个很简单,具体他怎么关闭的暂时不看了。接下来,我们大概有一个思路了:
首先,先定义好我们需要数据载体类实现INotification,再设计一个usercontrole,他的vm实现IInteractionRequestAware接口,然后在NotificationRequest.Raise的时候使用usercontrole,美滋滋!!!?
我们来通过客制化弹窗,实现从一个字符串列表中选择一个字符串,返回给父窗体:

先设计Notification,他并没有直接实现INotification,而是实现了IConfirmation,IConfirmationINotification的基础上,添加了一个Confirmed属性,来获取弹窗返回状态,是布尔型的(布尔型只有两种状态,很多时候,我需要有战斗机一般多的按钮的时候,他就不够用了,到时候得重新设计一个枚举类型的),这里,我们就直接实现IConfirmation为什么先是搞了一个接口呢?当然是为了依赖注入啊!依赖注入很难讲,以前我也看了很多大佬的资料,但是没有懂,后来去问大佬,大佬说,你看懂了吗?我说似懂非懂,他说,那就去看代码吧,慢慢的就懂了。?):

using Prism.Interactivity.InteractionRequest;

namespace UsingPopupWindowAction.Notifications
{
    public interface ICustomNotification : IConfirmation
    {
        string SelectedItem { get; set; }
    }
}

接着是我们的实现类(一个list(源),一个string(目标))继承 Confirmation实现我们的接口ICustomNotification,继承 Confirmation是因为他继承自Notification,而Notification是实现了INotification的,这样,我们就在我们的类里不用去实现INotification了,其实也可以不用继承·Confirmation·,完全可以自己实现ICustomNotification他所有的接口(话说若干年前我怎么记得接口不可以被继承只能被实现呢?记错了?):

using Prism.Interactivity.InteractionRequest;
using System.Collections.Generic;

namespace UsingPopupWindowAction.Notifications
{
    public class CustomNotification : Confirmation, ICustomNotification
    {
        public IList<string> Items { get; private set; }

        public string SelectedItem { get; set; }

        public CustomNotification()
        {
            this.Items = new List<string>();
            this.SelectedItem = null;

            CreateItems();
        }

        private void CreateItems()
        {
            //add some items
        }
    }
}

如果不继承Confirmation,则需要添加部分实现:


        public bool Confirmed { get ; set ; }
        public string Title { get ; set ; }
        public object Content { get ; set ; }

接下来设计我们的弹窗(一个列表(显示源),两个按钮,一个取消一个提交(获取目标)):

        <TextBlock Margin="10" TextWrapping="Wrap" FontWeight="Bold">Please select an item:</TextBlock>
        <ListBox SelectionMode="Single" Margin="10,0" Height="100" ItemsSource="{Binding Notification.Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}"></ListBox>

        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>

            <Button AutomationProperties.AutomationId="ItemsSelectButton" Grid.Column="0" Margin="10" Command="{Binding SelectItemCommand}">Select Item</Button>
            <Button AutomationProperties.AutomationId="ItemsCancelButton" Grid.Column="1" Margin="10" Command="{Binding CancelCommand}">Cancel</Button>
        </Grid>

弹窗的ViewModel,实现IInteractionRequestAware接口,这里设置了一个_notification来接收我们用来传递的那个类,这很像MVC里的Model,他只是个数据的载体,在每一个命令最后都需要关闭窗体,并且之前对confirmed和我们的SelectedItem进行赋值:
依旧省去了大部分代码,只看与我们有关的部分

    public class ItemSelectionViewModel : BindableBase, IInteractionRequestAware
    {
        public string SelectedItem { get; set; }
        private void CancelInteraction()
        {
            _notification.SelectedItem = null;
            _notification.Confirmed = false;
            FinishInteraction?.Invoke();
        }

        private void AcceptSelectedItem()
        {
            _notification.SelectedItem = SelectedItem;
            _notification.Confirmed = true;
            FinishInteraction?.Invoke();
        }

        public Action FinishInteraction { get; set; }

        private ICustomNotification _notification;

        public INotification Notification
        {
            get { return _notification; }
            set { SetProperty(ref _notification, (ICustomNotification)value); }
        }
    }
}

最后就是在Shell里调用这个客制化弹窗啦,跟之前的就一毛一样了,将INotification或者IConfirmation替换成我们的ICustomNotification,然后在new的时候使用CustomNotification,代码看上去应该是这个样子的:
xaml:

<prism:InteractionRequestTrigger SourceObject="{Binding CustomNotificationRequest}">
            <prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True">
                <prism:PopupWindowAction.WindowContent>
                    <views:ItemSelectionView />
                </prism:PopupWindowAction.WindowContent>
            </prism:PopupWindowAction>
        </prism:InteractionRequestTrigger>

viewmodel:

   public InteractionRequest<ICustomNotification> CustomNotificationRequest { get; set; }
        public DelegateCommand CustomNotificationCommand { get; set; }

        public MainWindowViewModel()
        {
            CustomNotificationRequest = new InteractionRequest<ICustomNotification>();
            CustomNotificationCommand = new DelegateCommand(RaiseCustomInteraction);
        }
        private void RaiseCustomInteraction()
        {
            CustomNotificationRequest.Raise(new CustomNotification { Title = "Custom Notification" }, r =>
                {
                    if (r.Confirmed && r.SelectedItem != null)
                        Title = $"User selected: { r.SelectedItem}";
                    else
                        Title = "User cancelled or didn't select an item";
                });
        }

最后一篇了!我会持续修正之前文章里理解偏颇的地方。谢谢大家!

转载于:https://www.cnblogs.com/hicolin/p/8804722.html

<think>好的,用户想要了解如何在Prism.WPF中使用DelegateCommand实现在ItemsControl中的RadioButton状态切换。首先,我需要回忆一下Prism框架中的DelegateCommand的用法,以及如何在ItemsControl中绑定RadioButton的状态。 首先,Prism的DelegateCommand用于将UI动作绑定到ViewModel中的命令。而ItemsControl通常用于显示集合数据,每个项可能对应一个RadioButton。这里的关键是让RadioButton的选中状态能够ViewModel中的某个属性同步,并且在选中时触发命令。 接下来,我应该考虑如何绑定RadioButton的IsChecked属性。通常,RadioButton在WPF中通过GroupName来分组,但在ItemsControl中,每个项可能属于同一组,这时候可能需要使用绑定到同一个属性,比如在ViewModel中有一个SelectedItem属性,每个RadioButton的IsChecked绑定到该属性当前项的对比。 然后,用户希望使用DelegateCommand来处理状态切换,这意味着当RadioButton被选中时,执行某个命令。可能需要将Command绑定到父元素(比如ItemContainerStyle中的某个事件),或者直接在RadioButton的Command属性上绑定。不过,RadioButton的Command通常是在Checked事件触发时执行,但需要确保命令的参数正确传递。 另外,考虑到ItemsControl的数据模板,每个RadioButton对应一个数据项,因此在命令执行时可能需要获取当前选中的项。可以通过CommandParameter绑定到当前的数据项,然后在ViewModel中处理命令时,更新SelectedItem属性。 可能的步骤: 1. 在ViewModel中定义一个ObservableCollection来保存ItemsControl的数据项。 2. 定义一个SelectedItem属性,用于绑定RadioButton的选中状态。 3. 创建一个DelegateCommand,用于处理选中状态改变时的逻辑。 4. 在XAML中,设置ItemsControl的ItemTemplate,包含RadioButton,其IsChecked绑定到SelectedItem,Command绑定到DelegateCommand,CommandParameter绑定到当前项。 5. 确保使用Prism的绑定语法,比如使用{prism:InvokeCommandAction}或者在Command属性中直接绑定。 需要注意的问题: - RadioButton的GroupName是否需要设置,或者通过绑定到同一个SelectedItem来自动处理分组。 - DelegateCommand的执行时机,是否在选中时触发,以及如何处理参数。 - 数据模板中的绑定上下文是否正确,确保Command能够正确指向ViewModel中的命令。 可能的代码结构: 在ViewModel中: public class MyViewModel : BindableBase { private MyItem _selectedItem; public MyItem SelectedItem { get => _selectedItem; set => SetProperty(ref _selectedItem, value); } public ObservableCollection<MyItem> Items { get; } = new ObservableCollection<MyItem>(); public DelegateCommand<MyItem> SelectItemCommand { get; } public MyViewModel() { SelectItemCommand = new DelegateCommand<MyItem>(item => SelectedItem = item); } } 在XAML中: <ItemsControl ItemsSource="{Binding Items}"> <ItemsControl.ItemTemplate> <DataTemplate> <RadioButton Content="{Binding Name}" IsChecked="{Binding IsSelected, Mode=TwoWay}" Command="{Binding DataContext.SelectItemCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}" CommandParameter="{Binding}"/> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> 或者,可能需要调整IsChecked的绑定,直接绑定到父ViewModel的SelectedItem,比如: IsChecked="{Binding DataContext.SelectedItem, RelativeSource={RelativeSource AncestorType=ItemsControl}, Converter={StaticResource EqualityConverter}, ConverterParameter={Binding}}" 这里可能需要一个EqualityConverter来比较当前项是否等于SelectedItem。 或者,在ViewModel中的每个MyItem对象包含一个IsSelected属性,这样可以直接绑定到该属性,但这样可能需要在集合中管理单选逻辑,可能不如使用父ViewModel的SelectedItem属性方便。 需要根据具体情况选择合适的方法。使用DelegateCommand的话,当RadioButton被选中时,命令会被触发,参数传递当前项,从而更新SelectedItem属性,这样IsChecked的绑定会自动更新。 可能需要测试不同的绑定方式,确保SelectedItem正确更新,并且命令被触发。同时,要处理可能的初始化状态,确保默认选中的项正确显示。</think>在 Prism.WPF 中使用 `DelegateCommand` 实现 `ItemsControl` 中 `RadioButton` 的状态切换,需要结合数据绑定和命令模式。以下是详细步骤和示例: --- ### **1. 核心思路** - **数据驱动**:通过 `ViewModel` 中的属性(如 `SelectedItem`)管理选中状态,`RadioButton` 的 `IsChecked` 属性绑定到该属性。 - **命令触发**:使用 `DelegateCommand` 在选中状态变化时执行逻辑(如更新状态或处理业务)。 - **数据模板**:在 `ItemsControl` 的 `DataTemplate` 中定义 `RadioButton`,绑定到集合中的每个项。 --- ### **2. ViewModel 定义** ```csharp public class MainViewModel : BindableBase { // 数据项集合 public ObservableCollection<string> Items { get; } = new ObservableCollection<string> { "Item 1", "Item 2", "Item 3" }; // 当前选中项 private string _selectedItem; public string SelectedItem { get => _selectedItem; set => SetProperty(ref _selectedItem, value); } // 切换选中状态的命令 public DelegateCommand<string> SelectItemCommand { get; } public MainViewModel() { SelectItemCommand = new DelegateCommand<string>(item => { SelectedItem = item; // 可以在此处添加额外逻辑,例如日志记录 }); } } ``` --- ### **3. XAML 实现** ```xml <Window x:Class="YourNamespace.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:prism="http://prismlibrary.com/" prism:ViewModelLocator.AutoWireViewModel="True"> <Grid> <!-- ItemsControl 显示集合 --> <ItemsControl ItemsSource="{Binding Items}"> <ItemsControl.ItemTemplate> <DataTemplate> <!-- RadioButton 定义 --> <RadioButton Content="{Binding}" GroupName="MyGroup" Margin="5" IsChecked="{Binding IsChecked, Mode=TwoWay}"> <!-- 通过 Interactivity 绑定命令 --> <i:Interaction.Triggers> <i:EventTrigger EventName="Checked"> <prism:InvokeCommandAction Command="{Binding DataContext.SelectItemCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}" CommandParameter="{Binding}" /> </i:EventTrigger> </i:Interaction.Triggers> </RadioButton> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Grid> </Window> ``` --- ### **4. 关键点说明** 1. **绑定 `IsChecked`** 直接绑定到 `ViewModel` 的 `SelectedItem` 可能更简洁(需使用 `ValueConverter` 对比当前项和选中项): ```xml IsChecked="{Binding DataContext.SelectedItem, RelativeSource={RelativeSource AncestorType=ItemsControl}, Converter={StaticResource EqualityConverter}, ConverterParameter={Binding}}" ``` `EqualityConverter` 需实现 `IValueConverter`,返回 `True` 当参数等于 `SelectedItem`。 2. **命令触发方式** - **方式1**:通过 `EventTrigger` 监听 `Checked` 事件,触发 `DelegateCommand`(需引用 `System.Windows.Interactivity`)。 - **方式2**:直接绑定 `RadioButton` 的 `Command` 属性到 `DelegateCommand`,但需注意 `IsChecked` 状态变化可能不会自动触发命令。 3. **参数传递** 使用 `CommandParameter="{Binding}"` 将当前数据项传递给命令,在 `ViewModel` 中处理选中逻辑。 --- ### **5. 补充代码(EqualityConverter)** ```csharp public class EqualityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return value?.Equals(parameter) ?? false; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return (bool)value ? parameter : null; } } ``` 在 XAML 中声明资源: ```xml <Window.Resources> <local:EqualityConverter x:Key="EqualityConverter" /> </Window.Resources> ``` --- ### **6. 最终效果** - 点击 `RadioButton` 时,`SelectedItem` 更新为当前项。 - 通过 `DelegateCommand` 可执行额外逻辑(如验证、导航等)。 - 界面状态 `ViewModel` 完全同步。 如有疑问,可进一步调整绑定方式或命令逻辑以满足具体需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值