在WPF中,是否可以为资源字典设置代码以处理事件?

160

在WPF中是否可以在资源字典中设置代码后台?例如,在用户控件中,您可以在XAML中声明按钮,而按钮点击的事件处理代码则在控件后面的代码文件中完成。如果我要创建一个包含按钮的数据模板,如何在资源字典中编写其按钮点击的事件处理程序代码。


1
正确的方法是使用一个命令,它还使您能够启用和禁用按钮,尽管您可以按照一些答案建议的方式进行操作,但在我看来这有点像黑客行为。 - Aran Mulholland
5个回答

234
我认为你所问的是要为ResourceDictionary创建一个code-behind文件。你完全可以做到这一点!实际上,你可以像为一个Window创建code-behind文件那样来做:
假设你有一个名为MyResourceDictionary的ResourceDictionary。在MyResourceDictionary.xaml文件中,把x:Class属性放在根元素中,就像这样:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    x:Class="MyCompany.MyProject.MyResourceDictionary"
                    x:ClassModifier="public">

接下来,创建一个名为MyResourceDictionary.xaml.cs的代码后台文件,并使用以下声明:

namespace MyCompany.MyProject
{
    partial class MyResourceDictionary : ResourceDictionary
    { 
       public MyResourceDictionary()
       {
          InitializeComponent();
       }     
       ... // event handlers ahead..
    }
}

操作完成。您可以在代码后台中放置任何您想要的内容:方法、属性和事件处理程序。

== Windows 10 应用程序更新 ==

如果您正在使用 UWP 进行开发,请注意以下一点:

<Application x:Class="SampleProject.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:rd="using:MyCompany.MyProject">
<!-- no need in x:ClassModifier="public" in the header above -->

    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>

                <!-- This will NOT work -->
                <!-- <ResourceDictionary Source="/MyResourceDictionary.xaml" />-->

                <!-- Create instance of your custom dictionary instead of the above source reference -->
                <rd:MyResourceDictionary />

            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>

</Application>

8
作为对ageektrapped回答的补充:确保在x:Class属性中放入您的代码后台类的完全限定名称。x:Class="MyCompany.MyProject.MySubFolder1.MyResourceDictionary",否则,如果您仅将x:Class设置为"MyResourceDictionary",XAML解析器将无法找到您的类。 - viggity
30
请确保在部分类代码后台提供默认构造函数,并确保它调用InitializeComponent()方法。(在我的情况下,我正在使用MEF将资源字典导出.) - Scott Whitlock
4
更新了赞同评论的代码片段。我觉得这很有必要来完善答案,因为这是一个常见的错误。我刚刚完成了这个更新 :)如果你不喜欢可以还原回去。感谢答案。 - Gishu
2
请注意,(至少在wp8.1中)这不再有效,您必须创建一个自定义用户控件,然后在您的资源字典中引用它。 - Jared
13
你还需要将ResourceDictionary的XAML文件的构建操作(Build Action)设置为"Page",否则InitializeComponent()调用将无法编译。(ResourceDictionary XAML文件默认通常设置为"Resource"。) - user1454265
显示剩余2条评论

10
我不同意“ageektrapped”的观点……使用部分类的方法并不是一个好的实践。那么将字典与页面分离的目的是什么呢?
从代码后台,您可以通过以下方式访问x:Name元素:
Button myButton = this.GetTemplateChild("ButtonName") as Button;
if(myButton != null){
   ...
}

如果您想在自定义控件加载时连接到控件,则可以在OnApplyTemplate方法中执行this。需要重写OnApplyTemplate才能这样做。这是一种常见的做法,可以使您的样式与控件分离。(样式不应该依赖于控件,但控件应该依赖于具有样式)。


8
我认为将词典与页面分离的目的是为了提高主页面 XAML 的可重用性和可读性。以上解决方案也适用于我。 - cleftheris

6

Gishu - 尽管这似乎是一种“通常不鼓励的做法”,但你可能想这样做的一个原因是:

当文本框获得焦点时,标准行为是将插入符放置在控件失去焦点时的相同位置。如果您希望在应用程序中始终如一地将整个文本框内容突出显示,那么在资源字典中添加一个简单的处理程序就可以解决问题。

任何其他需要默认用户交互行为与开箱即用行为不同的情况都是资源字典中代码后台的好选择。

完全同意,任何特定于应用程序功能的内容都不应该在资源字典的代码后台中。


0

另外,随着{x:Bind...}的出现,如果您想将DataTemplate放入共享的ResourceDictionary文件中,您需要为该文件提供一个代码后备。


-3

XAML 用于构建对象图,而不是包含代码。
数据模板用于指示如何在屏幕上呈现自定义用户对象(例如,如果它是列表框项),行为不是数据模板专业领域的一部分。重新绘制解决方案...


结论: 您会推荐使用资源字典与代码后端吗? 我从未使用过,我有所怀疑。 - Shimmy Weitzhandler
1
我个人认为不应该这样做,因为这种方式并不合适。字典应该返回特定键的值。在OP的情况下,将代码与数据模板捆绑在一起...我宁愿尝试不同的方法...例如使用命令模型。我需要更多关于OP问题的细节才能推荐一个不同的解决方案。 - Gishu
1
完全不同意。在MVVM中,有一种情况下,拥有代码后台非常有用:开发附加属性。先使用代码后台使其正常工作,然后再将其移植到附加属性中。这比从头开始开发附加属性要快得多,除非你的大脑像曼哈顿一样大。 - Contango

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接