在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>