在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的Page
和Page
没有名为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
将控件的属性看作是公开给外部世界使用的东西。
如果您希望使用自己的属性,则可以使用ElementName
或RelativeSource
绑定。
关于矫枉过正的事情, DependencyProperties
与DependencyObjects
;)
没有进一步的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>
您将需要定义它是可附加的属性来访问它像这样。