这听起来应该很简单。我在XAML中以正常方式声明了Page
(即使用“添加新项目...”)并且它具有自定义属性。我想在与页面关联的XAML中设置该属性。
尝试以与我设置任何其他属性相同的方式执行此操作不起作用,原因我理解但不知道如何工作。就这样我们有一些具体的话题,这里有一些(无效的)XAML。我尽可能地减少了所有东西 - 最初有一些属性,比如设计师的尺寸,但我相信那些与我正在做的事情无关。
<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名称空间中不存在属性“MyProperty” 'http://schemas.microsoft.com/winfx/2006/xaml/presentation'。第4行位置7.
现在我知道为什么会失败:元素的类型为Page
,而Page
没有名为MyProperty
的属性。这仅在TestPage
...中声明,由x:Class
属性指定,但不由元素本身指定。据我所知,XAML处理模型(即Visual Studio集成等)需要此配置。
我怀疑我可能使用依赖属性来处理这个问题,但这感觉有点像矫枉过正。我也可以使用现有属性(例如DataContext
),然后在稍后的代码中将值复制到自定义属性中,但这将非常难看。
以上是一个WPF示例,但我怀疑相同的答案将适用于Silverlight。我对两者都很感兴趣 - 所以如果你发布一个你知道会在一个而不是另一个中工作的答案,如果你在答案中指出答案,我将不胜感激:)
当有人发布一个非常简单的解决方案时,我正准备踢自己......
答案 0 :(得分:30)
如果为页面创建Base类,则可以使用不带Dependency属性的普通属性。
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不是Dependency或Attached,它仍然有效。
答案 1 :(得分:8)
你需要将其设为attachable property,如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>
然而,只有这个代码背后:
您将收到此错误:
可附加物业'MyProperty' 在'SkeetPage'类型中找不到。
附属物 'SkeetPage.MyProperty'未定义 在'Page'或其中一个基类。
修改强>
不幸的是,你必须使用依赖属性,她是一个有效的例子
网页强>
<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);
}
}
}
如果按下按钮,您将在MessageBox中看到“Testing ...”。
答案 2 :(得分:3)
您可以将<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()
和标准设计师的东西。但是,设计模式似乎仍然完美无缺,但我还没有对此进行过广泛的测试。
更新:此编译并运行,但实际上并未设置MyProperty
。您也失去了在XAML中绑定事件处理程序的能力(尽管可能有一种方法可以恢复我不知道的事件)。
更新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>
答案 3 :(得分:2)
您的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方法加载,the reference表示:
加载位于指定统一资源标识符(URI)的XAML文件,并将其转换为XAML文件的根元素指定的对象实例。
这意味着您只能为根元素指定的类型设置属性。因此,您需要子类化Page并将该子类指定为XAML的根元素。
答案 4 :(得分:1)
我的建议是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中,那就选择基类解决方案,或者上帝禁止,引入一个可附加的属性,也可以用在任何其他控件上。
答案 5 :(得分:1)
答案与Silverlight有关。
没有简单明显的方法以您想要的方式使用普通房产,在此过程中必须有一些妥协。
不起作用: -
有人建议依赖属性。这不会起作用,它仍然是Xaml POV的公共财产。附加属性将起作用,但这将使代码丑陋地使用它。
关闭但没有香蕉: -
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; }
}
然而,你失去了设计师的一些支持。值得注意的是,您必须创建字段以保存对命名元素的引用,并在您自己的InitialiseComponent
实现中自己分配它们(IMO所有这些自动字段的命名项目无论如何都不一定是好事)。此外,设计人员不会为您动态创建事件代码(虽然奇怪的是它似乎知道如何导航到您手动创建的事件代码),但Xaml中定义的事件将在运行时连接。
IMO最佳选择: -
最好的妥协已经由abhishek发布,使用垫片基类来保存属性。最小的努力,最大的兼容性。
答案 6 :(得分:1)
我只是尝试用不同的意图做同样的事情。
真正的答案实际上是:你需要正确完成Set-methods的WPF约定。 如下所述: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>
BTW:您还需要继承DependencyObject
答案 7 :(得分:1)
这对我有用
<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>
答案 8 :(得分:0)
您需要定义它是可附加属性才能像这样访问它。
答案 9 :(得分:0)
您可以使用以下样式设置属性:
<Page.Style>
<Style TargetType="{x:Type wpfSandbox:TestPage}">
<Setter Property="MyProperty" Value="This works" />
</Style>
</Page.Style>
但是它仅适用于依赖项属性!
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register(
nameof(MyProperty), typeof(string), typeof(Page),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
public string MyProperty
{
get { return (string)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}