在WPF/Silverlight页面中设置自定义属性
这听起来应该很简单。我有一个在WPF/Silverlight页面中设置自定义属性,wpf,xaml,silverlight,datacontext,Wpf,Xaml,Silverlight,Datacontext,这听起来应该很简单。我有一个页面以正常方式(即使用“添加新项…”)在XAML中声明,并且它有一个自定义属性。我想在与页面关联的XAML中设置该属性 试图以设置任何其他属性的相同方式来执行此操作是行不通的,原因我理解,但不知道如何解决。为了让我们有一些具体的东西要谈,这里有一些(无效的)XAML。我已经尽可能地减少了所有东西——最初有一些属性,比如设计师的尺寸,但我相信这些与我要做的事情无关 以及相应的隐藏代码: 使用System.Windows.Controls; 命名空间WpfSandbo
页面
以正常方式(即使用“添加新项…”)在XAML中声明,并且它有一个自定义属性。我想在与页面关联的XAML中设置该属性
试图以设置任何其他属性的相同方式来执行此操作是行不通的,原因我理解,但不知道如何解决。为了让我们有一些具体的东西要谈,这里有一些(无效的)XAML。我已经尽可能地减少了所有东西——最初有一些属性,比如设计师的尺寸,但我相信这些与我要做的事情无关
以及相应的隐藏代码:
使用System.Windows.Controls;
命名空间WpfSandbox{
公共部分类测试页:第页{
公共测试页(){
初始化组件();
}
公共字符串MyProperty{get;set;}
}
}
错误消息:
错误1 XML命名空间中不存在属性“MyProperty”
'http://schemas.microsoft.com/winfx/2006/xaml/presentation'. 第4行第7位
现在我知道了失败的原因:元素的类型是Page
,而Page
没有名为MyProperty
的属性。这只在TestPage
中声明。。。它由x:Class
属性指定,但不是由元素本身指定。据我所知,XAML处理模型(即Visual Studio集成等)需要此配置
我想我可以用依赖属性来处理这个问题,但这感觉有点过分了。我也可以使用一个现有属性(例如,DataContext
),然后在以后的代码中将该值复制到自定义属性中,但这会非常难看
上面是一个WPF示例,但我怀疑同样的答案也适用于Silverlight。我对两者都感兴趣——因此,如果你发布了一个答案,你知道这个答案在其中一个方面有效,但在另一个方面无效,如果你能在答案中指出这一点,我将不胜感激:)
当有人发布一个非常琐碎的解决方案时,我准备踢自己…您需要定义它是可附加的属性才能像这样访问它。您可以将您的
元素声明为
元素:
这样就可以了,但是你会失去InitializeComponent()
和标准的设计器功能。虽然设计模式似乎仍然可以完美地工作,但我还没有对此进行广泛的测试
更新:这会编译并运行,但不会实际设置MyProperty
。您还失去了在XAML中绑定事件处理程序的能力(尽管可能有一种方法可以还原我不知道的内容)
更新2:来自@Fredrik Mörk的工作示例,该示例设置属性,但不支持XAML中的绑定事件处理程序:
代码隐藏:
命名空间WpfApplication1
{
公共部分类主窗口:窗口
{
已激活受保护的覆盖无效(事件参数e)
{
this.Title=MyProperty;
}
公共字符串MyProperty{get;set;}
}
}
XAML:
您需要像Pavel所说的那样将它变成一个文件,然后您可以编写如下内容
但是,只有这段代码落后,您将得到以下错误:
可附加属性“MyProperty”
在类型“SkeetPage”中找不到
附属财产
未定义“SkeetPage.MyProperty”
在“Page”或其基类之一上
编辑 不幸的是,您必须使用依赖属性。这里有一个有效的例子 页面
代码隐藏
使用System.Windows;
使用System.Windows.Controls;
名称空间JonSkeetTest
{
公共部分类SkeetPage
{
公共密钥页()
{
初始化组件();
}
公共静态只读DependencyProperty MyPropertyProperty=DependencyProperty.Register(
“我的财产”,
类型(字符串),
类型(第页),
新的FrameworkPropertyMetadata(空,
FrameworkPropertyMetadataOptions.AffectsRender
)
);
公共静态void SetMyProperty(UIElement元素,字符串值)
{
元素.SetValue(MyPropertyProperty,value);
}
公共静态字符串GetMyProperty(UIElement)
{
返回元素.GetValue(MyPropertyProperty).ToString();
}
公共字符串MyProperty
{
get{return GetValue(MyPropertyProperty).ToString();}
set{SetValue(MyPropertyProperty,value);}
}
按下私有无效按钮测试(对象发送器,路由目标e)
{
MessageBox.Show(MyProperty);
}
}
}
如果按下按钮,您将在消息框中看到“Testing…”。如果为页面创建基类,则可以使用普通属性而不使用依赖属性
公共类基本窗口:窗口
{
公共字符串MyProperty{get;set;}
}
即使MyProperty不是依赖项或附件,它也可以工作。您的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>
MyPropertyValue
这显然是非法的。XAML文件由应用程序类的静态LoadComponent方法加载,并显示:
加载位于指定的统一资源标识符(URI)处的XAML文件,并将其转换为XAML文件的根元素指定的对象实例
这意味着您只能为根元素指定的类型设置属性。因此,您需要对Page进行子类化,并将该子类指定为XAML的根元素。我的建议是
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
<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; }
}
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); }
}
}
<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>
<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>
<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>