谁能为对话框提供WPF“可视继承”的具体示例?

时间:2019-10-09 19:24:35

标签: wpf inheritance

我是一位经验丰富的WinForms开发人员,对WPF来说还比较陌生。我有一个大型WinForms应用程序,它使用几个不同的基类来表示对话框。这样的例子就是AbstractOkCancelDialog。该类在对话框的底部包含一个面板,该面板的右侧带有“确定”和“取消”按钮。我正在尝试确定处理此问题的最佳方法,因为我意识到WPF不提供视觉继承。

我不想为应用程序中的每个对话框创建“确定”和“取消”按钮并将其放置。

我已经阅读到WPF中执行此操作的方法是使用用户控件。我可以设想创建一个带有“确定”和“取消”按钮的用户控件。但是,我不需要在应用程序中的数百个对话框上手动放置该用户控件。我真的很想要这样的东西:

'use strict';

require('dotenv').config();
const SpecReporter = require('jasmine-spec-reporter').SpecReporter;

let config = {
    framework: 'jasmine2',
    baseUrl: process.env.CLUSTER_URL,
    rootElement: 'body',

    onPrepare: function () {
        jasmine.getEnv().addReporter(new SpecReporter());
    },

    suites: {
        all: [
            'FoH/customerManagement/UI/*.smoke.spec.js',
            'Other/logo/logo.*.spec.js'
        ]
    },

    capabilities: {
        browserName: 'chrome',
        shardTestFiles: true,
        maxInstances: 2
    }
};

exports.config = config;

我在网上看到了一些有关如何创建基类的讨论。这些讨论解释了如何没有与对话框基类相关联的xaml文件,而且我了解该限制。我只是不知道如何使用“确定”和“取消”按钮自动放置用户控件。

我希望有人可以指出一个显示这种结构的示例解决方案。预先谢谢你!

3 个答案:

答案 0 :(得分:0)

编写一个对话框类。它是Window的子类。它具有XAML:

<Window
    ...blah blah blah...
    Title="{Binding Title}"
    >
    <StackPanel MinWidth="300">
        <!--  This is how you place content within content in WPF -->
        <ContentControl
            Content="{Binding}"
            Margin="2"
            />
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="2,20,2,2">
            <Button 
                Margin="2" 
                MinWidth="60" 
                DockPanel.Dock="Right" 
                Content="OK" 
                Click="OK_Click" 
                IsDefault="True" 
                />
            <Button 
                Margin="2" 
                MinWidth="60" 
                DockPanel.Dock="Right" 
                Content="Cancel" 
                IsCancel="True" 
                Click="Cancel_Click" 
                />
        </StackPanel>
    </StackPanel>
</Window>

您可以无限地欣赏它,但这是一个不错的下限,可以使您在一行右对齐按钮上方获得任意内容。根据需要添加更多按钮可能会涉及到模板窗口的一部分,或者使用ItemsControl(在生产代码中已经完成)或其他一些选项来创建它们。

用法:

var vm = new SomeDialogViewModel();
var dlg = new MyDialog { DataContext = vm };

对于每个对话框视图模型,使用者必须定义一个implicit datatemplate,以提供该视图模型的UI。

我建议编写一个希望消费者实现的对话框viewmodel接口。

public interface IDialogViewModel
{
    String Title { get; set; }
    void OnOK();

    //  Let them "cancel the cancel" if they like. 
    bool OnCancel();
}

该窗口可以检查其DataContext是否实现了该接口,并采取相应的措施。如果愿意,它可以要求该接口并抛出一个未实现的异常,或者只有在存在该接口的情况下才可以与之通信。如果他们没有实现它,但是仍然具有Title属性,则与Title的绑定仍然可以使用。绑定是“鸭式”的。

自然地,您可以编写一个OKCancelDialogViewModel或一个SelectStringFromListViewModel,编写相应的实现其UI的DataTemplates,并编写漂亮的干净静态方法来显示它们:

public static class Dialogs
{
    public static TOption Select<TOption>(IEnumerable<TOption> options, string prompt,
        string title = "Select Option") where TOption : class
    {
        //  Viewmodel isn't generic because that breaks implicit datatemplating.
        //  That's OK because XAML uses duck typing anyhow. 
        var vm = new SelectOptionDialogViewModel
        {
            Title = title,
            Prompt = prompt,
            Options = options
        };

        if ((bool)new Dialog { DataContext = vm }.ShowDialog())
        {
            return vm.SelectedOption as TOption;
        }

        return null;
    }

    // We have to call the value-type overload by a different name because overloads can't be 
    // distinguished when the only distinction is a type constraint. 
    public static TOption? SelectValue<TOption>(IEnumerable<TOption> options, string prompt,
        string title = "Select Option") where TOption : struct
    {
        var vm = new SelectOptionDialogViewModel
        {
            Title = title,
            Prompt = prompt,
            //  Need to box these explicitly
            Options = options.Select(opt => (object)opt)
        };

        if ((bool)new Dialog { DataContext = vm }.ShowDialog())
        {
            return (TOption)vm.SelectedOption;
        }

        return null;
    }
}

以下是上述选择对话框的一个视图模型数据模板:

<Application.Resources>
    <DataTemplate DataType="{x:Type local:SelectOptionDialogViewModel}">
        <StackPanel>
            <TextBlock
                TextWrapping="WrapWithOverflow"
                Text="{Binding Prompt}"
                />
            <ListBox
                ItemsSource="{Binding Options}"
                SelectedItem="{Binding SelectedOption}"
                MouseDoubleClick="ListBox_MouseDoubleClick"
                />
        </StackPanel>
    </DataTemplate>
</Application.Resources>

App.xaml.cs

private void ListBox_MouseDoubleClick(object sender, 
    System.Windows.Input.MouseButtonEventArgs e)
{
    ((sender as FrameworkElement).DataContext as IDialogViewModel).DialogResult = true;
}
var a = Dialogs.Select(new String[] { "Bob", "Fred", "Ginger", "Mary Anne" }, 
            "Select a dance partner:");
var b = Dialogs.SelectValue(Enum.GetValues(typeof(Options)).Cast<Options>(), 
            "Select an enum value:");

答案 1 :(得分:0)

下面是一个如何使用自定义AlertDialog的示例
  UserControl

<UserControl x:Class="Library.Views.AlertMessageDialogView"
             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" 
             xmlns:p="clr-namespace:Library.Properties" 
             DataContext="{Binding RelativeSource={RelativeSource Self}}"       
             FlowDirection = "{Binding WindowFlowDirection, Mode=TwoWay}">    

    <Grid Background="{DynamicResource WindowBackgroundBrush}">
        <Canvas HorizontalAlignment="Left" Height="145" VerticalAlignment="Top" Width="385">
            <Label HorizontalAlignment="Left" Height="57" VerticalAlignment="Top" Width="365" Canvas.Left="10" Canvas.Top="10" FontSize="14" >
                <TextBlock x:Name="txtVocabAnglais" TextWrapping="Wrap" Text="{Binding Message, Mode=TwoWay}" Width="365" Height="57"  />
            </Label>
            <Button x:Name="cmdCancel" Content="{x:Static p:Resources.AlertMessageDialogViewcmdCancel}" Height="30" Canvas.Left="163" Canvas.Top="72" Width="71" Command = "{Binding CancelCommand}" CommandParameter = "null" IsDefault="True"/>
        </Canvas>
    </Grid>

</UserControl>

ViewModel类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Windows;
using System.ComponentModel;
using System.Windows.Controls;

namespace Library.ViewModel
{
    public class AlertMessageDialogViewModel : BindableBaseViewModel
    {
        public event EventHandler CloseWindowEvent;

        private string _title;
        private string _message;

        public BaseCommand<string> YesCommand { get; private set; }
        public BaseCommand<string> CancelCommand { get; private set; }
        private WinformsNameSpace.FlowDirection _windowFlowDirection;

        public AlertMessageDialogViewModel()
        {
            CancelCommand = new BaseCommand<string>(cmdCancelBtnClick);
            WindowFlowDirection = CustomFuncVar.WindowFlowDirection;
        }

        public WinformsNameSpace.FlowDirection WindowFlowDirection
        {
            get
            {
                return _windowFlowDirection;
            }
            set
            {
                _windowFlowDirection = value;
                OnPropertyChanged("WindowFlowDirection");
            }
        }

        public string Message
        {
            get
            {
                return _message;
            }
            set
            {
                _message = value;
                OnPropertyChanged("Message");
            }
        }

        public string Title
        {
            get
            {
                return _title;
            }

            set
            {
                _title = value;
            }
        }

        private void cmdCancelBtnClick(string paramerter)
        {
            if (CloseWindowEvent != null)
                CloseWindowEvent(this, null);
        }

    }
}

DialogMessage类

using System;
using System.Windows;
using System.Collections.Generic;

namespace Library.Helpers
{
    public static class DialogMessage 
    {
        public static void AlertMessage(string message, string title, Window OwnerWindowView)
        {
            try
            {
                //Affichage de méssage de succès enregistrement
                AlertMessageDialogViewModel alertDialogVM = new AlertMessageDialogViewModel();
                alertDialogVM.Message = message;
                alertDialogVM.Title = title;

                // Auto Generation Window
                FrameworkElement view = LpgetCustomUI.AutoGetViewFromName("AlertMessageDialogView");
                view.DataContext = alertDialogVM;
                Dictionary<string, object> localVarWindowProperty = new Dictionary<string, object>();
                localVarWindowProperty = LpgetCustomUI.GetWindowPropretyType_400x145(Properties.Resources.ApplicationTitle);
                CummonUIWindowContainer alertDialogView = new CummonUIWindowContainer(view, null, false, localVarWindowProperty);
                //End Auto Generation Window

                // Attachement de l'évènement de fermture de View au modèle
                alertDialogVM.CloseWindowEvent += new EventHandler(alertDialogView.fnCloseWindowEvent);

                if (OwnerWindowView!=null)
                {
                    alertDialogView.Owner = OwnerWindowView;
                }
                else
                {
                    alertDialogView.WindowStartupLocation = WindowStartupLocation.CenterScreen;
                }

                alertDialogView.ShowDialog();
            }
            catch (Exception ex)
            {

            }
        }
    } 
}


CummonUIWindowContainer类

namespace CummonUILibrary.CummonUIHelpers
{
    public class CummonUIWindowContainer : Window
    {
        public event RoutedEventHandler CmbRootEvtLanguageChange;
        private FrameworkElement currentView;
        private ContentControl _contentcontainer;

        public CummonUIWindowContainer(string usercontrolName)
        {
            Contentcontainer = new ContentControl();
            currentView = new FrameworkElement();
        }

        public CummonUIWindowContainer()
        {
            Contentcontainer = new ContentControl();
            currentView = new FrameworkElement();
        }

        public CummonUIWindowContainer(FrameworkElement view, object model, bool setDataContextToView, Dictionary<string, object> WindowPropertyList)
        {
            Contentcontainer = new ContentControl();
            Contentcontainer.Name = "ContentControl";

            SetWindowProperty(view, model, setDataContextToView, WindowPropertyList);
        }

        public void SetWindowProperty(FrameworkElement view, object model, bool setDataContextToView, Dictionary<string, object> WindowPropertyList)
        {
            try
            {

                LinearGradientBrush brush = new LinearGradientBrush();
                GradientStop gradientStop1 = new GradientStop();
                gradientStop1.Offset = 0;
                gradientStop1.Color = Colors.Yellow;
                brush.GradientStops.Add(gradientStop1);

                GradientStop gradientStop2 = new GradientStop();
                gradientStop2.Offset = 0.5;
                gradientStop2.Color = Colors.Indigo;
                brush.GradientStops.Add(gradientStop2);
                GradientStop gradientStop3 = new GradientStop();
                gradientStop3.Offset = 1;
                gradientStop3.Color = Colors.Yellow;
                brush.GradientStops.Add(gradientStop3);
                this.Background = brush;

                CurrentView = view;
                Type elementType = this.GetType();
                ICollection<string> WindowPropertyListNames = WindowPropertyList.Keys;

                foreach (string propertyName in WindowPropertyListNames)
                {
                    PropertyInfo property = elementType.GetProperty(propertyName);
                    property.SetValue(this, WindowPropertyList[propertyName]);                    
                }

                if (setDataContextToView == true & model != null)
                {
                    CurrentView.DataContext = model;
                }
                if (CurrentView != null)
                {
                    Contentcontainer.Content = CurrentView;
                }

                //Contentcontainer.Margin = new Thickness(0,0, 0, 0);
                IAddChild container=this;
                container.AddChild(Contentcontainer);                

            }
            catch (Exception ex)
            {
            }
        }

        public void fnCloseWindowEvent(object sender, EventArgs e)
        {
            this.Close();
        }

        public ContentControl Contentcontainer
        {
            get
            {
                return _contentcontainer;
            }
            set
            {
                _contentcontainer = value;
            }
        }

        public FrameworkElement CurrentView
        {
            get
            {
                return currentView;
            }
            set
            {
                if (this.currentView != value)
                {
                    currentView = value;
                    //RaisePropertyChanged("CurrentView");
                }
            }
        }   

        private void cmbLanguage_SelectionChanged(object sender, RoutedEventArgs e)
        {
            //CmbRootEvtLanguageChange(sender, e);
        }

    }
}

如何使用课程

DialogMessage.AlertMessage("My Custom Message", "My Custom Title Message");

答案 2 :(得分:0)

那是我会怎么做

为您的对话框创建一个抽象基类,并更改相应的ControlTemplate

AbstractOkCancelDialog

public abstract class AbstractOkCancelDialog : Window
{
    public static readonly DependencyProperty CancelCommandParameterProperty =
        DependencyProperty.Register(
            "CancelCommandParameter",
            typeof(object),
            typeof(AbstractOkCancelDialog),
            new FrameworkPropertyMetadata((object) null));

    public static readonly DependencyProperty CancelCommandProperty =
        DependencyProperty.Register(
            "CancelCommand",
            typeof(ICommand),
            typeof(AbstractOkCancelDialog),
            new FrameworkPropertyMetadata((ICommand) null));

    public static readonly DependencyProperty OkCommandParameterProperty =
        DependencyProperty.Register(
            "OkCommandParameter",
            typeof(object),
            typeof(AbstractOkCancelDialog),
            new FrameworkPropertyMetadata((object) null));

    public static readonly DependencyProperty OkCommandProperty =
        DependencyProperty.Register(
            "OkCommand",
            typeof(ICommand),
            typeof(AbstractOkCancelDialog),
            new FrameworkPropertyMetadata((ICommand) null));


    static AbstractOkCancelDialog()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(AbstractOkCancelDialog), new
            FrameworkPropertyMetadata(typeof(AbstractOkCancelDialog)));
    }

    public ICommand CancelCommand
    {
        get => (ICommand) GetValue(CancelCommandProperty);
        set => SetValue(CancelCommandProperty, value);
    }

    public object CancelCommandParameter
    {
        get => GetValue(CancelCommandParameterProperty);
        set => SetValue(CancelCommandParameterProperty, value);
    }

    public ICommand OkCommand
    {
        get => (ICommand) GetValue(OkCommandProperty);
        set => SetValue(OkCommandProperty, value);
    }

    public object OkCommandParameter
    {
        get => GetValue(OkCommandParameterProperty);
        set => SetValue(OkCommandParameterProperty, value);
    }
}

样式

放入Generic.xaml [?]

<Style
    BasedOn="{StaticResource {x:Type Window}}"
    TargetType="{x:Type local:AbstractOkCancelDialog}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:AbstractOkCancelDialog}">
                <Border
                    Background="{TemplateBinding Background}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="{TemplateBinding BorderThickness}">
                    <AdornerDecorator>
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="*" />
                                <RowDefinition Height="Auto" />
                            </Grid.RowDefinitions>
                            <ContentPresenter />
                            <Grid Grid.Row="1">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*" />
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="Auto" />
                                </Grid.ColumnDefinitions>
                                <Button
                                    Grid.Column="1"
                                    Margin="5"
                                    Command="{TemplateBinding OkCommand}"
                                    CommandParameter="{TemplateBinding OkCommandParameter}"
                                    Content="Ok"
                                    DockPanel.Dock="Right" />
                                <Button
                                    Grid.Column="2"
                                    Margin="5"
                                    Command="{TemplateBinding CancelCommand}"
                                    CommandParameter="{TemplateBinding CancelCommandParameter}"
                                    Content="Cancel"
                                    DockPanel.Dock="Right" />
                            </Grid>
                        </Grid>
                    </AdornerDecorator>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

现在您可以像创建其他任何窗口一样创建自己的对话框

简要示例:

TestDialog.xaml

<local:AbstractOkCancelDialog
    x:Class="WpfApp.TestDialog"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:WpfApp"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="TestDialog"
    Width="800"
    Height="450"
    OkCommand="{x:Static local:Commands.OkWindowCommand}"
    OkCommandParameter="{Binding RelativeSource={RelativeSource Self}}"
    CancelCommand="{x:Static local:Commands.CancelWindowCommand}"
    CancelCommandParameter="{Binding RelativeSource={RelativeSource Self}}"
    mc:Ignorable="d">
    <Grid>
        <!-- Content -->
    </Grid>
</local:AbstractOkCancelDialog>

TestDialog.xaml.cs

public partial class TestDialog : AbstractOkCancelDialog
{
    ...
}