Erstellen Sie wiederverwendbare dynamische Ansichten

Unser Team möchte wiederverwendbare, anpassbare Ansichten erstellen. Zum Beispiel wollen wir das in verschiedenen Apps wiederverwendenCommonPromptView (In unserem eigenen, anpassbaren Dialogfeld können wir die Schaltfläche "Abbrechen" ausblenden, die Kopfzeile festlegen, ein bestimmtes Symbol anzeigen usw.).

Diese Ansicht hat mehrere Elemente auf ihrer Oberfläche: TextBlocks, Buttons. Wir wollen sie dazu stylbar machen.

Was ist der beste Ansatz, um diese Aufgabe zu lösen?

Die Ansicht kann vom Typ Fenster sein.Die Ansicht kann vom Typ UserControl sein.

Im ersten Fall kann das Stylen auf zwei Arten unterstützt werden:

Stile von Elementen haben Verweise auf DynamicResources.Stile werden an den Konstruktor einer Ansicht übergeben.

Die beiden sind nicht sauber (meiner Meinung nach).

Wenn es sich bei einer Ansicht um ein UserControl handelt, muss jedes Mal, wenn jemand eine neue App erstellt, ein neues Fenster mit einem UserControl erstellt und die Bindungen für DPs (vom Stiltyp) ordnungsgemäß festgelegt werden. Außerdem, wenn ein UserControl eine eigene, sehr praktische API hat (statische Methoden für die am häufigsten verwendeten Operationen), die für den Benutzer des Fensters, das das UserControl enthält, verloren geht.

Update

Beispiel vonCommonPromptView als UserControl implementiert.

Code-behind

 public sealed partial class CommonPromptView {
    private const int CloseViewTimeIntervalInMilliseconds = 120000;
    private DispatcherTimer timer;

    public static readonly DependencyProperty CommonPromptBorderStyleProperty = DependencyProperty.Register(
        "CommonPromptBorderStyle", typeof (Style), typeof (CommonPromptView), new PropertyMetadata(default(Style)));

    public Style CommonPromptBorderStyle {
        get { return (Style) GetValue(CommonPromptBorderStyleProperty); }
        set { SetValue(CommonPromptBorderStyleProperty, value); }
    }

    public static readonly DependencyProperty CommonPromptHeaderStyleProperty = DependencyProperty.Register(
        "CommonPromptHeaderStyle", typeof (Style), typeof (CommonPromptView), new PropertyMetadata(default(Style)));

    public Style CommonPromptHeaderStyle {
        get { return (Style) GetValue(CommonPromptHeaderStyleProperty); }
        set { SetValue(CommonPromptHeaderStyleProperty, value); }
    }

    public static readonly DependencyProperty CommonPromptMessageStyleProperty = DependencyProperty.Register(
        "CommonPromptMessageStyle", typeof (Style), typeof (CommonPromptView), new PropertyMetadata(default(Style)));

    public Style CommonPromptMessageStyle {
        get { return (Style) GetValue(CommonPromptMessageStyleProperty); }
        set { SetValue(CommonPromptMessageStyleProperty, value); }
    }

    public static readonly DependencyProperty CommonPromptSpitterStyleProperty = DependencyProperty.Register(
        "CommonPromptSpitterStyle", typeof (Style), typeof (CommonPromptView), new PropertyMetadata(default(Style)));

    public Style CommonPromptSpitterStyle {
        get { return (Style) GetValue(CommonPromptSpitterStyleProperty); }
        set { SetValue(CommonPromptSpitterStyleProperty, value); }
    }

    public static readonly DependencyProperty CommonPromptButtonStyleProperty = DependencyProperty.Register(
        "CommonPromptButtonStyle", typeof (Style), typeof (CommonPromptView), new PropertyMetadata(default(Style)));

    public Style CommonPromptButtonStyle {
        get { return (Style) GetValue(CommonPromptButtonStyleProperty); }
        set { SetValue(CommonPromptButtonStyleProperty, value); }
    }

    [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public CommonPromptView(object header = null, string message = "Вы действительно хотите продолжить?",
        Visibility headerVisibility = Visibility.Collapsed,
        MessageBoxIconWPF iconType = MessageBoxIconWPF.Question,
        object affirmButtonContent = null, object cancelButtonContent = null,
        Visibility cancelButtonVisibility = Visibility.Visible, Visibility affirmButtonVisibility = Visibility.Visible) {
        InitializeComponent();
        Header.Content = header ?? string.Empty;
        if (header == null)
            HeaderSplitter.Visibility = Visibility.Collapsed;
        Message.Content = message;

        Ok.Content = affirmButtonContent ?? "ОК";
        Cancel.Content = cancelButtonContent ?? "Отмена";

        Cancel.Visibility = cancelButtonVisibility;
        Header.Visibility = headerVisibility;
        Ok.Visibility = affirmButtonVisibility;

        BitmapImage icon = new BitmapImage();
        icon.BeginInit();
        icon.UriSource = new Uri(GetIconPath(iconType));
        //new Uri("pack://application:,,,/ApplicationName;component/Resources/logo.png");
        icon.EndInit();

        Icon.Source = icon;

        SetTimer();
    }

    private static string GetIconPath(MessageBoxIconWPF icon) {
        const string uri = "pack://application:,,,/Microtech.WPF.Common;component/";
        string iconName;
        switch (icon) {
            case MessageBoxIconWPF.Error:
                iconName = "CustomDialogStop";
                break;
            case MessageBoxIconWPF.Information:
                iconName = "CustomDialogInformation";
                break;
            case MessageBoxIconWPF.Question:
                iconName = "CustomDialogQuestion";
                break;
            case MessageBoxIconWPF.Warning:
                iconName = "CustomDialogWarning";
                break;
            default:
                throw new ArgumentException("There were no such an image");
        }
        return uri + string.Format("Images/{0}.{1}", iconName, "png");
    }

    public CommonPromptView(string content, Visibility cancelButtonVisibility = Visibility.Visible)
        : this(message: content, cancelButtonVisibility: cancelButtonVisibility) {
    }

    private void SetTimer() {
        TimeSpan timerInterval = TimeSpan.FromMilliseconds(CloseViewTimeIntervalInMilliseconds);
        timer = new DispatcherTimer(timerInterval, DispatcherPriority.ApplicationIdle,
            (obj, e) => Cancel_Click(null, null),
            Dispatcher.CurrentDispatcher);
        timer.Start();
    }

    public CommonPromptView() {
        InitializeComponent();
    }

    public static bool PromptOnUserAgreement(string header, string message, string okText = "Да",
        string cancelText = "Нет") {
        return new CommonPromptView(header,
            message, Visibility.Visible, MessageBoxIconWPF.Information, okText, cancelText).ShowDialog()
            .GetValueOrDefault();
    }

    public static void PromptOnWarning(string header, string message) {
        new CommonPromptView(header, message, headerVisibility: Visibility.Visible, iconType: MessageBoxIconWPF.Warning,
            cancelButtonVisibility: Visibility.Collapsed).ShowDialog();
    }

    public static void PromptOnError(string header, string message) {
        new CommonPromptView(header, message, headerVisibility: Visibility.Visible, iconType: MessageBoxIconWPF.Error,
            cancelButtonVisibility: Visibility.Collapsed).ShowDialog();
    }

    public static void PromptOnSuccess(string header, string message) {
        new CommonPromptView(header, message, headerVisibility: Visibility.Visible,
            iconType: MessageBoxIconWPF.Information,
            cancelButtonVisibility: Visibility.Collapsed).ShowDialog();
    }

    private void Ok_Click(object sender, RoutedEventArgs e) {
        StopTimer();
        TryCloseTheWindow(true);            
    }

    private void Cancel_Click(object sender, RoutedEventArgs e) {
        StopTimer();
        TryCloseTheWindow(false);
    }

    private void TryCloseTheWindow(bool dialogResult) {
        Window parentwin = GetWindow(this);
        if (parentwin != null) {
            try {
                parentwin.DialogResult = dialogResult;
            } catch (InvalidOperationException) {

            }
            parentwin.Close();
        }
    }

    private void StopTimer() {
        if (timer != null) {
            timer.Stop();
            timer = null;
        }
    }
}

XAML

<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"                  
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             x:Class="Microtech.WPF.Common.CommonPromptView"
             Background="Transparent" 
             DataContext="{Binding RelativeSource={RelativeSource Self}}">

<UserControl.Resources>
    <Style x:Key="DefaultViewStyle" TargetType="Window">
        <Setter Property="ResizeMode" Value="NoResize" />
        <Setter Property="ShowInTaskbar" Value="False" />
        <Setter Property="WindowStyle" Value="None" />
        <Setter Property="WindowState" Value="Normal" />
        <Setter Property="SizeToContent" Value="WidthAndHeight" />
        <Setter Property="Topmost" Value="True" />
        <Setter Property="Cursor" Value="Arrow" />
    </Style>

    <Style x:Key="DefaultHeaderStyle" TargetType="Label">
        <Setter Property="Margin" Value="10,5,5,5"/>
        <Setter Property="HorizontalAlignment" Value="Left" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="FontSize" Value="20" />
        <Setter Property="FontWeight" Value="Bold" />
    </Style>

    <Style x:Key="DefaultBorderStyle" TargetType="Border">
        <Setter Property="Background" Value="#ADAAAD"/>
        <Setter Property="BorderBrush" Value="Black" />
        <Setter Property="Padding" Value="10" />
        <Setter Property="BorderThickness" Value="3"/>
        <Setter Property="CornerRadius" Value="10"/>          
    </Style>

    <Style x:Key="DefaultMessageStyle" TargetType="Label">
        <Setter Property="Margin" Value="10"/>
        <Setter Property="FontSize" Value="14"/>
        <Setter Property="FontFamily" Value="Verdana"/>
        <Setter Property="FontWeight" Value="Normal"/>
    </Style>

    <Style x:Key="DefaultSplitterStyle" TargetType="GridSplitter">
        <Setter Property="HorizontalAlignment" Value="Stretch" />
        <Setter Property="VerticalAlignment" Value="Bottom" />
        <Setter Property="BorderThickness" Value="0.65" />
        <Setter Property="BorderBrush" Value="Black" />
        <Setter Property="IsEnabled" Value="False" />
    </Style>

    <Style x:Key="FStandartButton" TargetType="{x:Type Button}">
        <Setter Property="Background" Value="{x:Null}" />
        <Setter Property="BorderBrush" Value="#00000000" />
        <Setter Property="Foreground" Value="White" />
        <Setter Property="VerticalContentAlignment" Value="Center" />
        <Setter Property="Height" Value="Auto" />
        <Setter Property="MinHeight" Value="55" />
        <Setter Property="Width" Value="420" />
        <Setter Property="Margin" Value="5" />
        <Setter Property="Padding" Value="10" />
        <Setter Property="FontSize" Value="22" />
        <Setter Property="FontWeight" Value="Bold" />
        <Setter Property="FocusVisualStyle" Value="{x:Null}" />

        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Border x:Name="border" BorderBrush="#FF000000" BorderThickness="1,1,1,1" CornerRadius="4,4,4,4">
                        <Border.Background>
                            <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                                <GradientStop Offset="0" Color="#8C8A8C" />
                                <GradientStop Offset="1" Color="#636163" />
                            </LinearGradientBrush>
                        </Border.Background>
                        <ContentPresenter Name="ContentContainer" 
                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                          RecognizesAccessKey="True"
                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsPressed" Value="True">
                            <Setter TargetName="border" Property="Background" Value="#CC000000" />
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter Property="Foreground" Value="Gray" />
                        </Trigger>

                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="DefaultButtonStyle" TargetType="Button" BasedOn="{StaticResource FStandartButton}">
        <Setter Property="FontFamily" Value="Verdana"/>
        <Setter Property="FontSize" Value="14"/>
        <Setter Property="Margin" Value="6"/>
    </Style>
</UserControl.Resources>

<Border Style="{Binding CommonPromptBorderStyle, TargetNullValue={StaticResource DefaultBorderStyle}}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Label x:Name="Header"
               Style="{Binding CommonPromptHeaderStyle, TargetNullValue={StaticResource DefaultHeaderStyle}}" />
        <GridSplitter x:Name="HeaderSplitter" Grid.Row="0" 
                      Style="{Binding CommonPromptSpitterStyle, TargetNullValue={StaticResource DefaultSplitterStyle}}"
                      />

        <StackPanel Grid.Row="1" Margin="5,10,5,0" Orientation="Horizontal">
            <Image x:Name="Icon" Width="32" Height="32" Margin="5" 
                   HorizontalAlignment="Left"
                   VerticalAlignment="Center" />
            <Label x:Name="Message" 
                   Style="{Binding CommonPromptMessageStyle, 
                           TargetNullValue={StaticResource DefaultMessageStyle}}" />
        </StackPanel>

        <StackPanel Grid.Row="2" HorizontalAlignment="Center" Orientation="Horizontal">
            <Button x:Name="Ok" 
                    Style="{Binding CommonPromptButtonStyle, TargetNullValue={StaticResource DefaultButtonStyle}}"  
                    Click="Ok_Click" />
            <Button x:Name="Cancel" 
                    Style="{Binding CommonPromptButtonStyle, TargetNullValue={StaticResource DefaultButtonStyle}}"  
                    IsDefault="True" Click="Cancel_Click" />
        </StackPanel>
    </Grid>
</Border>

Antworten auf die Frage(1)

Ihre Antwort auf die Frage