如何创建一个通用的WPF基本窗口样式?

时间:2011-09-21 23:37:06

标签: wpf xaml

是否有任何推荐的WPF方法可以创建一个在应用程序中使用的通用窗口样式?我有几个对话框出现在我的应用程序中,我希望它们的样式相同(相同的窗口边框,确定/取消按钮位置等),并根据情况简单地在每个中都有不同的“内容”。因此,一个对话框中可能包含一个列表框,一个可能有一个文本框,依此类推。

我理解如何制作基本.cs用户控制文件,但我不能为我的生活找到一个好方法来创建一个可以在启动时托管不同内容的窗口?

干杯, RJ

5 个答案:

答案 0 :(得分:16)

一种方法是使用新的自定义控件,我们称之为DialogShell

namespace Test.Dialogs
{
    public class DialogShell : Window
    {
        static DialogShell()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(DialogShell), new FrameworkPropertyMetadata(typeof(DialogShell)));
        }
    }
}

现在需要一个通常在Themes/Generic.xaml中定义的模板,在那里你可以创建默认结构并绑定Content

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Test.Dialogs">
    <Style TargetType="{x:Type local:DialogShell}" BasedOn="{StaticResource {x:Type Window}}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:DialogShell}">
                    <Grid Background="{TemplateBinding Background}">
                        <Grid.RowDefinitions>
                            <RowDefinition />
                            <RowDefinition Height="Auto" />
                        </Grid.RowDefinitions>
                        <!-- This ContentPresenter automatically binds to the Content of the Window -->
                        <ContentPresenter />
                        <StackPanel Grid.Row="1" Orientation="Horizontal" Margin="5" HorizontalAlignment="Right">
                            <Button Width="100" Content="OK" IsDefault="True" />
                            <Button Width="100" Content="Cancel" IsCancel="True" />
                        </StackPanel>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

这只是一个示例,您可能希望将这些按钮与您需要在cs文件中定义的自定义事件和属性相连接。

然后可以像这样使用shell:

<diag:DialogShell x:Class="Test.Dialogs.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:diag="clr-namespace:Test.Dialogs"
        Title="Window1" Height="300" Width="300">
    <Grid>
        <TextBlock Text="Lorem Ipsum" />
    </Grid>
</diag:DialogShell>
namespace Test.Dialogs
{
    public partial class Window1 : DialogShell
    {
        public Window1()
        {
            InitializeComponent();
        }
    }
}

事件接线示例(不确定这是否是“正确”方法)

<Button Name="PART_OKButton" Width="100" Content="OK" IsDefault="True" />
<Button Name="PART_CancelButton" Width="100" Content="Cancel" IsCancel="True" />
namespace Test.Dialogs
{
    [TemplatePart(Name = "PART_OKButton", Type = typeof(Button))]
    [TemplatePart(Name = "PART_CancelButton", Type = typeof(Button))]
    public class DialogShell : Window
    {
        //...

        public DialogShell()
        {
            Loaded += (_, __) =>
                {
                    var okButton = (Button)Template.FindName("PART_OKButton", this);
                    var cancelButton = (Button)Template.FindName("PART_CancelButton", this);
                    okButton.Click += (s, e) => DialogResult = true;
                    cancelButton.Click += (s, e) => DialogResult = false;
                };
        }
    }
}

答案 1 :(得分:2)

要添加到HB非常有用的帖子,您可能希望在加载的事件中连接事件处理程序,但不要使用匿名方法或lambda表达式,请考虑将它们连接到可以覆盖的受保护虚拟方法派生类应该功能需要改变。在我的例子中,我创建了一个基本数据输入表单,其中包含用于保存和取消的按钮:

    public DataEntryBase()
    {
        Loaded += (_, __) =>
        {
            var saveButton = (Button)Template.FindName("PART_SaveAndCloseButton", this);
            var cancelButton = (Button)Template.FindName("PART_CancelButton", this);
            saveButton.Click += SaveAndClose_Click;
            cancelButton.Click += Cancel_Click;
        };
    }

    protected virtual void SaveAndClose_Click(object sender, RoutedEventArgs e) { DialogResult = true; }

    protected virtual void Cancel_Click(object sender, RoutedEventArgs e) { }

然后在每个派生类中重写保存功能以保存特定实体:

    protected override void SaveAndClose_Click(object sender, RoutedEventArgs e)
    {
        if (Save())
        {
            base.SaveAndClose_Click(sender, e);
        }
    }

    private bool Save()
    {
        Contact item = contactController.SaveAndReturnContact((Contact)DataContext);
        if (item!=null) 
        {
            DataContext = item;
            return true; }
        else 
        {
            MessageBox.Show("The contact was not saved, something bad happened :(");
            return false;
        }            
    }

答案 2 :(得分:0)

您可以在App.Xaml中使用定义一个针对所有窗口的样式。

这是App.Xaml可能如下所示的示例:

<Application x:Class="ES.UX.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         StartupUri="Views/MainWindow.xaml">
<Application.Resources>
    <Style TargetType="Window">
        <Setter Property="WindowStyle" Value="ToolWindow" />
    </Style>
</Application.Resources>

然后,对于更高级的场景,您可能需要为Window设置ControlTemplate。

答案 3 :(得分:0)

创建一个自定义对象,该对象派生自Window Class ..

http://maffelu.net/wpf-window-inheritance-problems-and-problems/

答案 4 :(得分:0)

创建Xaml表单模板并将模板添加到VS Installed ItemTemplates目录。

1)创建一个wpf xaml和xaml.cs文件,该文件包含添加到应用程序中的新表单所需的所有所需组件。在我的情况下,我想要标题和工具栏按钮。

2)通过当前系统流测试新的xaml文件。

3)将xaml / xaml.cs复制到临时位置,并将这两个文件名重命名为您想要识别为良好模板名称的内容。  a)将xaml文件中的第一行更改为 -                   Window x:Class =“$ rootnamespace $。$ safeitemname $”

b)在xaml.cs文件中进行3次更改,以确保在使用模板时复制新名称 - - namespace $ rootnamespace $(//动态命名空间名称) - public partial class $ safeitemname $(//动态类名) - public $ safeitemname $()(//动态构造函数名称)

4)现在创建一个vstemplate文件:即。 MyTemplate.vstemplate包含以下内容:

<VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Item">
  <TemplateData>
    <DefaultName>WpfFormTemplate.xaml</DefaultName>
    <Name>WpfFormTemplate</Name>
    <Description>Wpf/Entities form</Description>
    <ProjectType>CSharp</ProjectType>
    <SortOrder>10</SortOrder>
    <Icon>Logo.ico</Icon>
  </TemplateData>
  <TemplateContent>
    <References>
        <Reference>
            <Assembly>System.Windows.Forms</Assembly>
        </Reference>
        <Reference>
            <Assembly>Workplace.Data.EntitiesModel</Assembly>
        </Reference>
        <Reference>
            <Assembly>Workplace.Forms.MainFormAssemb</Assembly>
        </Reference>
    </References>
    <ProjectItem SubType="Designer" TargetFileName="$fileinputname$.xaml" ReplaceParameters="true">WpfFormTemplate.xaml</ProjectItem>
    <ProjectItem SubType="Code" TargetFileName="$fileinputname$.xaml.cs" ReplaceParameters="true">WpfFormTemplate.xaml.cs</ProjectItem>
  </TemplateContent>
</VSTemplate>

5)获得所有这些文件后,压缩文件并将zip文件放在.... \ Documents \ Visual Studio 2012 \ Templates \ ItemTemplates \ WPF目录下。现在您可以进入VS2012并使用ADD \ New功能查看模板,选择并重命名为正常过程。通过将zip文件放在2010 Templates Wpf目录下,可以以相同的方式为VS2010使用该模板。

徽标文件也应包含在zip文件中,或者如果您没有文件,则从MyTemplate.vstemplate文件中删除该行。