Prism 彈出視窗

【君莫笑】發表於2024-09-30

1、簡單實現
①、建立使用者控制元件

跨模組的視窗彈出,只需要建立視窗的內容即可,也就是使用者控制元件,這裡是在Views資料夾下,建立DialogContentView使用者控制元件。

需要注意的是,預設情況下,如果需要對彈出視窗進行樣式設定的話,需要透過prism:Dialog.WindowStyle來進行設定。

<UserControl x:Class="Zhaoxi.PrismDialog.Views.DialogContentView" 
                         xmlns:prism="http://prismlibrary.com/"
                         ......>
    <prism:Dialog.WindowStyle>
        <Style TargetType="Window">
            <Setter Property="Width" Value="500"/>
            <Setter Property="Height" Value="400"/>
            <!--設定WindowStyle方式的無邊款-->
            <!--<Setter Property="WindowStyle" Value="None"/>
            <Setter Property="AllowsTransparency" Value="True"/>-->
            <!--設定WindowChrome的無邊款-->
            <Setter Property="WindowChrome.WindowChrome">
                <Setter.Value>
                    <WindowChrome GlassFrameThickness="-1"/>
                </Setter.Value>
            </Setter>
        </Style>
    </prism:Dialog.WindowStyle>
</UserControl>

②、建立ViewModel

在ViewModels資料夾中建立DialogContentViewModel類並實現IDialogAware介面。

public class DialogContentViewModel : IDialogAware
{
    public string Title => "跨域彈出來的子視窗標題";

    //關閉彈窗的操作
    public event Action<IDialogResult> RequestClose;

    //是否允許關閉視窗
    public bool CanCloseDialog()
    {
        //可以根據情況做一下業務判斷
        return true;
    }
    //視窗關閉或主動觸發RequestClose事件時呼叫
    public void OnDialogClosed()
    {
        
    }
    //視窗開啟時呼叫
    public void OnDialogOpened(IDialogParameters parameters)
    {
        
    }
}

③、註冊View

在App的RegisterTypes方法中透過呼叫IContainerRegistry物件的RegisterDialog方法進行DialogContentView的註冊

RegisterDialog<TView>(string name = null):註冊對話型別,可以傳入string作為使用時的key。

需要注意的是,如果沒有傳入name引數,使用ShowDialog展示時,則根據註冊的對話型別名稱來查詢。如果傳入了name,則必須根據name來查詢。

public partial class App : PrismApplication
{
    protected override Window CreateShell()
    {
        return Container.Resolve<Views.MainWindow>();
    }

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
              //註冊一個視窗型別
        containerRegistry.RegisterDialog<Views.DialogContentView>();
    }
}

④、定義彈出視窗命令

透過IOC依賴注入,獲得IDialogService屬性物件。

定義命令,在命令中呼叫IDialogService物件的ShowDialog方法來展示串列埠。

ShowDialog(string name):展示模態對話方塊,如果註冊時,指定了對話名稱,則根據名稱在IOC的對話容器中查詢。否則就根據對話型別的名稱進行查詢。至於對話的父類視窗,預設情況下會自動使用Prism內建的預設視窗物件。

Show(string name):的用法與ShowDialog(string name)用法一樣,只不過展示的是非模態對話方塊。

public class MainWindowViewModel:BindableBase
{
    private string _btnContent = "跨域彈窗";

    public string BtnContent
    {
        get { return _btnContent; }
        set 
        {
            SetProperty(ref _btnContent, value);
        }
    }

        //IOC依賴注入
    [Dependency]
    public IDialogService _dialogService { get; set; }

    public ICommand BtnCommand 
    {
        get => new DelegateCommand(() =>
        {
                      //展示視窗
            _dialogService.ShowDialog("DialogContentView");
        });
    }
}

⑤、主視窗中繫結命令

<Window ......>
    <Grid>
        <StackPanel>
            <Button Content="{Binding BtnContent}" Command="{Binding BtnCommand}"/>
        </StackPanel>
    </Grid>
</Window>

2、註冊父類視窗
在上面的實現過程中,我們只建立了使用者控制元件,而使用者控制元件的父類容器,即彈出來的視窗物件則使用了Prism中預設的Dialog視窗。也因此,預設情況下,如果需要對彈出的視窗進行設定,要在使用者控制元件中透過prism:Dialog.WindowStyle來進行預設視窗的樣式設定。

然而透過prism:Dialog.WindowStyle來進行視窗的樣式設定的做法本來就不是很符合xaml的設計規範,而且這隻能對當前的使用者控制元件起效果,如果有多個控制元件希望進行統一的視窗樣式處理的話,在每個使用者控制元件中都編寫一樣的程式碼會顯得很冗餘。較好的解決方案就是重新建立並設定好一個父類窗體,然後註冊到IOC的對話視窗容器中。

成功為對話設定了父類視窗後,父類視窗的樣式就可以跟平常的WPF視窗一樣直接在XAML中進行設定了。其在XAML中設定的樣式的優先順序高於prism:Dialog.WindowStyle。

①、建立父類窗體

在程式集中新建DialogBase資料夾(資料夾名稱並不重要,甚至可以不用建立)並在其中建立DialogWindowBase視窗。

在後臺程式碼實現IDialogWindow,簡化一下Result屬性

<Window ......
        WindowStartupLocation="CenterScreen" Background="Transparent"
        ResizeMode="NoResize"
        Title="DialogWindowBase" Height="300" Width="500">
    <WindowChrome.WindowChrome>
        <WindowChrome GlassFrameThickness="-1"/>
    </WindowChrome.WindowChrome>
    <Grid>

    </Grid>
</Window>
public partial class DialogWindowBase : Window, IDialogWindow
{
    public DialogWindowBase()
    {
        InitializeComponent();
    }

    public IDialogResult Result { get; set; }
}

②、註冊父類窗體型別

透過RegisterDialogWindow進行對話父類視窗型別的註冊後,會優先使用,而不會自動使用預設的對話視窗型別物件。

RegisterDialogWindow<TWindow>([string name]):註冊對話的父類視窗型別。

當註冊多個父類視窗型別時,可以傳入name方便後面展示時透過key來指定視窗型別,需要注意註冊時一旦指定了name,展示時就必須要指定name,否則找不到對應的父類視窗物件。
需要注意的是,要進行註冊的對話父類視窗的後臺程式碼中都必須實現IDialogWindow介面,否則無法註冊。

public partial class App : PrismApplication
{
    protected override Window CreateShell()
    {
        return Container.Resolve<Views.MainWindow>();
    }

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        //註冊一個對話型別
        containerRegistry.RegisterDialog<DialogContentView>();
        //註冊一個對話父類視窗型別
        containerRegistry.RegisterDialogWindow<DialogWindowBase>();
        //還可以註冊多個父類視窗型別,然後根據key來指定
    }
}

③、展示視窗

透過IOC依賴注入,獲得IDialogService屬性物件。

定義命令,在命令中呼叫IDialogService物件的ShowDialog方法來展示串列埠。

  • 當註冊的對話父類視窗型別只有一個且沒有指定名稱時,prism會自動去使用,因此只要指定對話型別即可。

public class MainWindowViewModel:BindableBase
{
    ......
    [Dependency]
    public IDialogService _dialogService { get; set; }

    public ICommand BtnCommand 
    {
        get => new DelegateCommand(() =>
        {
            _dialogService.ShowDialog("DialogContentView");
        });
    }
}

3、指定父類窗體

當註冊了多個父類窗體時,可以透過名稱來區分。

①、註冊多個父類視窗

public partial class App : PrismApplication
{
    protected override Window CreateShell()
    {
        return Container.Resolve<Views.MainWindow>();
    }

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        //註冊一個對話型別
        containerRegistry.RegisterDialog<DialogContentView>("DialogOne");
        containerRegistry.RegisterDialog<DialogContentView>("DialogTwo"); //這裡為了方便就不再建立一個新的使用者控制元件了
        //註冊一個對話父類視窗型別
        containerRegistry.RegisterDialogWindow<DialogWindowBase>("windowBase");
        //可以註冊多個父類視窗型別,然後根據key來指定
        containerRegistry.RegisterDialogWindow<DialogWindowOne>("windowOne");
    }
}

②、指定展示

需要注意的是,如果註冊的對話指定了名稱,展示時沒有使用匹配的名稱會導致找不到而報異常;如果註冊的窗體型別都指定了名稱,

展示時沒有指定名稱則會使用prism框架中預設的窗體型別。

public class MainWindowViewModel:BindableBase
{
    ......

    [Dependency]
    public IDialogService _dialogService { get; set; }

    public ICommand BtnCommand 
    {
        get => new DelegateCommand(() =>
        {
                        //指定展示的對話及父類窗體
            _dialogService.ShowDialog("DialogOne",null,null, "windowBase");
        });
    }
}

4、資料傳遞
當需要向彈出窗體的內容(即我們建立的使用者控制元件)傳遞一些資料的時候,可以透過ShowDialog方法來實現。

①、傳遞資料

ShowDialog(string name, IDialogParameters parameters, Action<IDialogResult> callback, string windowName):展示對話視窗。

parameters:向對話視窗傳遞的資料物件。
DialogParameters:IDialogParameters的子類,用法跟字典是一樣的,可以用DialogParameters物件封裝資料後傳遞給對話視窗。

public class MainWindowViewModel:BindableBase
{
    ......

    [Dependency]
    public IDialogService _dialogService { get; set; }

    public ICommand BtnCommand 
    {
        get => new DelegateCommand(() =>
        {
                        //建立資料載體
            var dialogParameters = new DialogParameters();
            dialogParameters.Add("paramKey", "paramValue");
                        //展示視窗並進行資料傳遞
            _dialogService.ShowDialog("DialogOne", dialogParameters, null, "windowBase");
        });
    }
}

②、資料處理

傳遞的資料物件會在視窗開啟時,傳遞給對應的IDialogAware子類物件(一般就是彈出視窗的ViewModel)的OnDialogOpened方法。因此,可以在OnDialogOpened方法中對資料進行處理。

public class DialogContentViewModel : BindableBase,IDialogAware
{
    ......

    public void OnDialogOpened(IDialogParameters parameters)
    {
        PassValue = parameters.GetValue<string>("paramKey");
    }

    private string _passValue;

    public string PassValue
    {
        get { return _passValue; }
        set 
        { 
             SetProperty(ref _passValue, value);
        }
    }
}

5、回撥函式
當彈出的視窗關閉時需要執行回撥函式,也是透過ShowDialog方法來實現的。

①、設定回撥函式

ShowDialog(string name, IDialogParameters parameters, Action<IDialogResult> callback, string windowName):展示對話視窗。

callback:對話視窗的回撥函式,為一個接收IDialogResult型別引數的Action物件。設定後會自動掛載到對應的IDialogAware子類物件(一般就是彈出視窗的ViewModel)RequestClose事件上,也就是可以透過RequestClose事件來呼叫callback回撥函式。

需要注意的是RequestClose事件的觸發,會先呼叫視窗的關閉函式,再執行回撥函式。對話視窗的正常關閉也會執行回撥函式。

public class MainWindowViewModel:BindableBase
{
        ......
    [Dependency]
    public IDialogService _dialogService { get; set; }

    public ICommand BtnCommand 
    {
        get => new DelegateCommand(() =>
        {
            var dialogParameters = new DialogParameters();
            dialogParameters.Add("paramKey", "paramValue");
            _dialogService.ShowDialog("DialogOne", dialogParameters, DialogCallBack, "windowBase");
        });
    }

    //對話視窗的回撥函式
    private void DialogCallBack(IDialogResult result)
    {
        //可以根據傳過來的result做業務邏輯處理
    }
}

②、呼叫回撥函式
在對應的ViewModel上定義命令,觸發RequestClose事件。

 public DelegateCommand CloseCommand { get; set; }
        public DialogContentViewModel()
        {
            CloseCommand = new DelegateCommand(OnClose);
        }
        private void OnClose()
        {
            IDialogResult dialogResult = new DialogResult();
            dialogResult.Parameters.Add("A", true);
            RequestClose?.Invoke(dialogResult);
        }
來源:https://blog.csdn.net/jjailsa/article/details/135972127

相關文章