This sounds like it should be simple. I have a Page
declared in XAML in the normal way (i.e. with "Add new item...") and it has a custom property. I\'
You can work with normal property without Dependency property if you create a Base class for your Page.
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>
And it works even though MyProperty is not a Dependency or Attached.
Answer relates to Silverlight.
There is no simple obvious way to use plain property in the way you want, there will have to be some compromise along the way.
Doesn't really work:-
Some suggest a dependency property. That won't work, its still a public property from Xaml POV. An attached property will work but that would make working with it in code ugly.
Close but no banana:-
The Xaml and the class can be fully separated like this:-
<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>
Code:-
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; }
}
However you lose some support from the designer. Notably you will have to create the fields to hold references to named elements and assign them yourself in your own implementation of InitialiseComponent
(IMO all these automatic fields for named items isn't necessarily a good thing anyway). Also the designer won't create event code dynamically for you (although strangely it seems to know how to navigate to one you have manually created) however events defined in Xaml will be wired up at runtime.
IMO best option:-
The best compromise has already been posted by abhishek, use a shim base class to hold the properties. Minimul effort, maximum compatibility.
My suggestion would be a DependencyProperty
with a default:
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
See the properties of controls as something you expose to the outside world to use.
If you wish to use your own property, you can use either ElementName
or RelativeSource
Bindings.
About the overkill thing, DependencyProperties
go hand in hand with DependencyObjects
;)
No further XAML needed, the default in the PropertyMetadata
will do the rest.
If you really wish to put it in the XAML, go for the base class solution, or gods forbid, introduce an attachable property, which can be used on any other control as well.
Your XAML is equivalent of the following:
<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>
This is obviously illegal. The XAML-file is being loaded by the static LoadComponent method of the Application class, and the reference says:
Loads a XAML file that is located at the specified uniform resource identifier (URI) and converts it to an instance of the object that is specified by the root element of the XAML file.
That means that you can only set properties for the type specified by the root element. So you need to subclass Page and specify that subclass as the root element of you XAML.
You would need to define it is attachable property to access it like this.
You would need to make it an attachable property as Pavel noted, then you can write something like this
<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>
However, with only this code-behind, you will get this error instead:
The attachable property 'MyProperty' was not found in type 'SkeetPage'.
The attached property 'SkeetPage.MyProperty' is not defined on 'Page' or one of its base classes.
Edit
Unfortunately, you have to use Dependency Properties. Here's a working example
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>
Code-behind
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);
}
}
}
If you press the button, you will see "Testing..." in a MessageBox.