在WPF / Silverlight页面中设置自定义属性

这听起来应该是简单的。 我有一个正常的方式在XAML中声明的Page (即“添加新项目…”),它有一个自定义属性。 我想在与页面关联的XAML中设置该属性。

试图做同样的方式,我设置任何其他财产不起作用,原因我明白,但不知道如何工作。 就这样我们有一些具体的讨论,这里有一些(无效的)XAML。 我尽可能地减less了所有的东西 – 原来有一些属性,比如devise师的大小,但我相信这些与我正在做的事情无关。

 <Page x:Class="WpfSandbox.TestPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" MyProperty="MyPropertyValue"> </Page> 

和相应的代码隐藏:

 using System.Windows.Controls; namespace WpfSandbox { public partial class TestPage : Page { public TestPage() { InitializeComponent(); } public string MyProperty { get; set; } } } 

错误信息:

错误1 XML名称空间“ http://schemas.microsoft.com/winfx/2006/xaml/presentation ”中不存在属性“MyProperty”。 4号线位置7。

现在我知道为什么这是失败的:元素是types的PagePage没有名为MyProperty属性。 这只是在TestPage声明的…它由x:Class属性指定,而不是由元素本身指定。 据我所知,这个configuration是XAML处理模型(即Visual Studio集成等)所必需的。

我怀疑我可以用一个依赖属性处理这个,但是这有点像矫枉过正。 我也可以使用现有的属性(例如DataContext ),然后将值复制到代码后面的自定义属性中,但这会非常难看。

以上是WPF示例,但我怀疑在Silverlight中也会应用相同的答案。 我对这两个都很感兴趣,所以如果你发表了一个你知道可以在其中工作但不是另一个的答案,我会很感激,如果你在答案中指出的话:)

当某人发布一个绝对简单的解决scheme时,我准备踢自己…

如果您为页面创build基类,则可以使用不具有“依赖”属性的普通属性。

  public class BaseWindow : Window { public string MyProperty { get; set; } } <local:BaseWindow x:Class="BaseWindowSample.Window1" x:Name="winImp" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:BaseWindowSample" MyProperty="myproperty value" Title="Window1" Height="300" Width="300"> </local:BaseWindow> 

即使MyProperty不是一个依赖或附加它工作。

你需要使它成为Pavel指出的可附加属性 ,然后你可以写这样的东西

 <Page x:Class="JonSkeetTest.SkeetPage" 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:JonSkeetTest="clr-namespace:JonSkeetTest" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300" JonSkeetTest:SkeetPage.MyProperty="testar" Title="SkeetPage"> <Grid> </Grid> </Page> 

但是只有这个代码:

你会得到这个错误,而不是:

在“SkeetPage”types中找不到附加属性“MyProperty”。

附加属性“SkeetPage.MyProperty”没有在“页面”或其基类之一上定义。


编辑

不幸的是,你必须使用依赖属性,她是一个工作的例子

 <Page x:Class="JonSkeetTest.SkeetPage" 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:JonSkeetTest="clr-namespace:JonSkeetTest" mc:Ignorable="d" JonSkeetTest:SkeetPage.MyProperty="Testing.." d:DesignHeight="300" d:DesignWidth="300" Title="SkeetPage"> <Grid> <Button Click="ButtonTest_Pressed"></Button> </Grid> </Page> 

守则背后

 using System.Windows; using System.Windows.Controls; namespace JonSkeetTest { public partial class SkeetPage { public SkeetPage() { InitializeComponent(); } public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register( "MyProperty", typeof(string), typeof(Page), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender ) ); public static void SetMyProperty(UIElement element, string value) { element.SetValue(MyPropertyProperty, value); } public static string GetMyProperty(UIElement element) { return element.GetValue(MyPropertyProperty).ToString(); } public string MyProperty { get { return GetValue(MyPropertyProperty).ToString(); } set { SetValue(MyPropertyProperty, value); } } private void ButtonTest_Pressed(object sender, RoutedEventArgs e) { MessageBox.Show(MyProperty); } } } 

如果按下button,您将在MessageBox中看到“Testing …”。

你可以声明你的<Page>元素是一个<TestPage>元素:

 <YourApp:TestPage xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:YourApp="clr-namespace:YourApp" MyProperty="Hello"> </YourApp:TestPage> 

这将做的伎俩,但你失去了InitializeComponent()和标准的devise师的东西。 虽然devise模式似乎仍然完美地工作,但我还没有广泛的testing。

更新:这编译和运行,但实际上并没有设置MyProperty 。 您也失去了在XAML中绑定事件处理程序的能力(尽pipe可能有办法恢复我不知道的事情)。

更新2:来自@FredrikMörk的工作示例,它设置属性,但不支持XAML中的绑定事件处理程序:

代码后面:

 namespace WpfApplication1 { public partial class MainWindow : Window { protected override void OnActivated(EventArgs e) { this.Title = MyProperty; } public string MyProperty { get; set; } } } 

XAML:

 <WpfApplication1:MainWindow xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:WpfApplication1="clr-namespace:WpfApplication1" Title="MainWindow" Height="350" Width="525" MyProperty="My Property Value"> </WpfApplication1:MainWindow> 

您的XAML相当于以下内容:

 <Page x:Class="SkeetProblem.TestPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Page.MyProperty>MyPropertyValue</Page.MyProperty> </Page> 

这显然是非法的。 XAML文件正在被Application类的静态LoadComponent方法加载, 引用说:

加载位于指定统一资源标识符(URI)的XAML文件,并将其转换为由XAML文件的根元素指定的对象的实例。

这意味着您只能设置由根元素指定的types的属性。 因此,您需要对Page进行子类化,并将该子类指定为XAML的根元素。

答案与Silverlight有关。

用你想要的方式使用普通财产没有简单明显的方法,一路上也不得不妥协。

没有真正的工作: –

一些build议依赖属性。 这是行不通的,它仍然是Xaml POV的公共财产。 一个附加的属性将工作,但这将使代码丑陋的工作。

closures,但没有香蕉: –

Xaml和类可以像这样完全分离:

 <local:PageWithProperty xmlns:local="clr-namespace:StackoverflowSpikes" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" Message="Hello World" Loaded="PageWithProperty_Loaded" Title="Some Title" > <Grid x:Name="LayoutRoot"> <TextBlock Text="{Binding Parent.Message, ElementName=LayoutRoot}" /> </Grid> </local:PageWithProperty> 

码:-

 public class PageWithProperty : Page { internal System.Windows.Controls.Grid LayoutRoot; private bool _contentLoaded; public void InitializeComponent() { if (_contentLoaded) { return; } _contentLoaded = true; System.Windows.Application.LoadComponent(this, new System.Uri("/StackoverflowSpikes;component/PageWithProperty.xaml", System.UriKind.Relative)); this.LayoutRoot = ((System.Windows.Controls.Grid)(this.FindName("LayoutRoot"))); } public PageWithProperty() { InitializeComponent(); } void PageWithProperty_Loaded(object sender, RoutedEventArgs e) { MessageBox.Show("Hi"); } public string Message {get; set; } } 

但是,你失去了devise师的一些支持。 值得注意的是,您将不得不创build字段来保存对命名元素的引用,并将它们自己分配给您自己的InitialiseComponent实现(IMO对于命名项的所有这些自动字段不一定是好东西)。 另外,devise者不会为你dynamic地创build事件代码(尽pipe奇怪的是它似乎知道如何导航到你手动创build的),但是在Xaml中定义的事件将在运行时连接起来。

海事组织最佳select: –

abhishek已经发布了最好的折衷办法,用shim基类来保存这些属性。 最小的努力,最大的兼容性。

我的build议将是一个默认的DependencyProperty

  public int MyProperty { get { return (int)GetValue(MyPropertyProperty); } set { SetValue(MyPropertyProperty, value); } } public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register("MyProperty", typeof(int), typeof(MyClass), new PropertyMetadata(1337)); //<-- Default goes here 

将控件的属性看作是公开给外部世界使用的东西。

如果您希望使用自己的属性,则可以使用ElementNameRelativeSource绑定。

关于矫枉过正的事情, DependencyPropertiesDependencyObjects ;)

没有进一步的XAML需要, PropertyMetadata的默认值将完成剩下的工作。

如果你真的想把它放在XAML中,那么去基类解决scheme,或者上帝禁止,引入一个可以附加的属性,这个属性也可以用在任何其他控件上。

不过,我试图用一些不同的意图来做同样的事情。

真正的答案实际上是:您需要WPF约定Set-methods正确完成。 如此处所述: http : //msdn.microsoft.com/en-us/library/ms749011.aspx#custom如果要定义名为Xxx的附加属性,则必须定义SetXxx和GetXxx方法。

所以看到这个工作的例子:

 public class Lokalisierer : DependencyObject { public Lokalisierer() { } public static readonly DependencyProperty LIdProperty = DependencyProperty.RegisterAttached("LId", typeof(string), typeof(Lokalisierer), new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(OnLocIdChanged))); private static void OnLocIdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // on startup youll be called here } public static void SetLId(UIElement element, string value) { element.SetValue(LIdProperty, value); } public static string GetLId(UIElement element) { return (string)element.GetValue(LIdProperty); } public string LId { get{ return (string)GetValue(LIdProperty); } set{ SetValue(LIdProperty, value); } } } 

而WPF部分:

 <Window x:Class="LokalisierungMitAP.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:me="clr-namespace:LokalisierungMitAP" Title="LokalisierungMitAP" Height="300" Width="300" > <StackPanel> <Label me:Lokalisierer.LId="hhh">Label1</Label> </StackPanel> 

顺便说一句:你还需要inheritanceDependencyObject

这对我有效

 <Window x:Class="WpfSandbox.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfSandbox" xmlns:src="clr-namespace:WpfSandbox" Title="MainWindow" Height="350" Width="525" src:MainWindow.SuperClick="SuperClickEventHandler"> </Window> 

所以这可能适用于原来的问题(没有尝试)。 注意xmlns:src。

 <Page x:Class="WpfSandbox.TestPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfSandbox" xmlns:src="clr-namespace:WpfSandbox" src:TestPage.MyProperty="MyPropertyValue"> </Page> 

您将需要定义它是可附加的属性来访问它像这样。