WPF, Silverlight, XAML, and Dependency Properties

In my opinion, XAML has a bit of a step learning curve.  I tend to jump head first into something and do my best to learn it.  For some stuff, my head strong, fly-by-the-seat-of-my-pants, style of learning works, XAML was a bit rougher.  I think part of it was I was expected it to work just like HTML which it doesn’t.  I’d pick up a book and play with Expression Blend to help see what does what.  Visual Studio 2010’s updated editor for WPF and Silverlight projects helps a lot as well. 

So why even care about XAML?  This lets you abstract out your views which then lets you be able to update your UI with no repercussions to your data.  Do a Model View ViewModel style of work.  XAML had dependency properties that helps simplify your backend code.  Depending on how something is bound to a field on the page, it will update.  This is really powerful.  The amount of code I didn’t need to write in Drinktendr due to using XAML to manage this really did make a big difference.  With the new Channel9 version coming out the door, Coding4Fun is getting moved over there which means bringing 5 years worth of posts, comments, and source code examples over.  And I plan to do some additional work on the meta data for the posts.  So with modifying BlogML and Meta Weblog To BlogML Converter projects, I created an application to get the data from the blogs.msdn.com platform, get it into blogml as a middle ground data format, then will port it to Channel9’s data format.

So to do this, I created a horrid UI that only I care about.

image

So what is neat here is looking at the code.

from my mainwindow.xaml:

<Controls:LoadAndSave Grid.Column="0" x:Name="loadAndSave"/>
<Controls:ListPosts Grid.Column="1" x:Name="listPosts"
	Posts="{Binding ElementName=loadAndSave, Path=Posts }"/>
<Controls:PostData Grid.Column="3"
	SelectedPost="{Binding ElementName=listPosts, Path=SelectedPost }"/>
<Controls:ListComments Grid.Column="5" 
	Comments="{Binding ElementName=listPosts, Path=SelectedPost.Comments }"/>
<Controls:WebData Grid.Column="7" 
	SelectedPost="{Binding ElementName=listPosts, Path=SelectedPost }"/>

All I’m doing is passing around object references with data binding!  When something gets updated, it is automatically reflected back (* depending on how you have your binding set up).

To create a dependency property, in your code behind, type “propdp” and hit tab twice.  This will be a snippet in Visual Studio that then you can type everything else out.  Here is what the base will look like.

public int MyProperty
{
	get { return (int)GetValue(MyPropertyProperty); }
	set { SetValue(MyPropertyProperty, value); }
}

// Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
	DependencyProperty.Register("MyProperty", typeof(int), typeof(ownerclass), new UIPropertyMetadata(0));

So for the more complex example of me updating the WebData control with the Webbrowser controls, here is my XAML for that control and here is the code behind.  Since WebBrowser uses a method rather than a property to update the content in it, the backend view has a bit more code.

XAML:

<UserControl x:Class="c4fDataPort.Gui.Controls.WebData"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Controls="clr-namespace:c4fDataPort.Gui.Controls">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
			<ColumnDefinition Width="Auto"/> <!-- 1 split -->
			<ColumnDefinition Width="*" />
			<ColumnDefinition Width="Auto"/> <!-- 3 split -->
			<ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
		<GridSplitter Grid.Column="1" Style="{DynamicResource gridSplit}" />
		<GridSplitter Grid.Column="3" Style="{DynamicResource gridSplit}" />
		<Grid Grid.Column="0">
			<Grid.RowDefinitions>
				<RowDefinition Height="{Binding ElementName=menu, Path=Height}" />
				<RowDefinition Height="*" />
			</Grid.RowDefinitions>
			<Label Name="menu" Style="{DynamicResource sectionTitles}">Current Render:</Label>
			<WebBrowser Name="initialWebBrowser"  Grid.Row="1"/>
        </Grid>
		<Grid Grid.Column="2">
			<Grid.RowDefinitions>
				<RowDefinition Height="{Binding ElementName=menu2, Path=Height}" />
				<RowDefinition Height="*" />
			</Grid.RowDefinitions>
			<Label Name="menu2" Style="{DynamicResource sectionTitles}">Edited HTML:</Label>
			<TextBox Grid.Row="1" TextWrapping="WrapWithOverflow" VerticalScrollBarVisibility="Auto"
					 Text="{Binding Path=PostText, RelativeSource={RelativeSource AncestorType={x:Type Controls:WebData}}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
		</Grid>
		<Grid Grid.Column="4">
			<Grid.RowDefinitions>
				<RowDefinition Height="{Binding ElementName=menu3, Path=Height}" />
				<RowDefinition Height="*" />
			</Grid.RowDefinitions>
			<Label Name="menu3" Style="{DynamicResource sectionTitles}">Final Render:</Label>
			<WebBrowser Name="finalWebBrowser" Grid.Row="1" />
		</Grid>
		
    </Grid>
</UserControl>

CodeBehind:

using System.Windows;
using System.Windows.Controls;

using BlogML.Xml;

namespace c4fDataPort.Gui.Controls
{
	/// <summary>
	/// Interaction logic for WebData.xaml
	/// </summary>
	public partial class WebData : UserControl
	{
		public WebData()
		{
			InitializeComponent();
		}


		private string PostId;
		public string PostText
		{
			get { return (string)GetValue(PostTextProperty); }
			set { SetValue(PostTextProperty, value); }
		}
			
		// Using a DependencyProperty as the backing store for PostText.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty PostTextProperty =
			DependencyProperty.Register("PostText", typeof(string), typeof(WebData), new UIPropertyMetadata("", HtmlChanged));

		public BlogMLPost SelectedPost
		{
			get { return (BlogMLPost)GetValue(SelectedPostProperty); }
			set { SetValue(SelectedPostProperty, value); }
		}

		// Using a DependencyProperty as the backing store for SelectedPost.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty SelectedPostProperty =
			DependencyProperty.Register("SelectedPost", typeof(BlogMLPost), typeof(WebData), new UIPropertyMetadata(new BlogMLPost(), PostChanged));

		private static void PostChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
		{
			var typedSender = sender as WebData;
			if (typedSender == null)
				return;

			typedSender.SetInitalPanel();
		}

		private static void HtmlChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
		{
			var typedSender = sender as WebData;
			if (typedSender == null)
				return;

			typedSender.SetFinalPanel();
		}

		private void SetInitalPanel()
		{
			if (SelectedPost.ID == PostId)
				return;

			if (!string.IsNullOrEmpty(SelectedPost.Content.Text))
				initialWebBrowser.NavigateToString(SelectedPost.Content.Text);

			PostText = SelectedPost.Content.Text;
			PostId = SelectedPost.ID;
		}

		private void SetFinalPanel()
		{
			if (!string.IsNullOrEmpty(PostText))
				finalWebBrowser.NavigateToString(PostText);

			SelectedPost.Content.Text = PostText;
		}
	}
}

Then based on certain values, you can do more complex stuff like change the background color or hide stuff.

In this project, if a post is marked “Clean”, the post item is green.  Also based on the post type, additional data will be shown.  Here is how I did that:

XAML:

<UserControl.Resources>
	<Controls:BoolToColorConverter x:Key="backgroundColorConverter" />
</UserControl.Resources>
<StackPanel Background="{Binding Path=SelectedPost.IsDataCleaned, RelativeSource={RelativeSource AncestorType={x:Type Controls:ListPostItem}}, Converter={StaticResource backgroundColorConverter}}" >
	<!-- more stuff -->
</StackPanel>

CodeBehind:

[ValueConversion(typeof(bool), typeof(SolidColorBrush))]
public class BoolToColorConverter : IValueConverter
{
	public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
	{
		return ((bool)value) ? new SolidColorBrush(Color.FromRgb(239, 255, 220)) : new SolidColorBrush(Color.FromRgb(255, 204, 204));
	}

	public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
	{
		throw new NotImplementedException();
	}
}

The more I play with XAML, the more I really do appreciate the power even though there were days were I did hate it.  Also after playing with VS 2010, I do think a lot of my learning issues were solved with additional intellisense features.

Eric Sep 25, 2013 @ 3:12 AM

# re: WPF, Silverlight, XAML, and Dependency Properties
It's difficult to control all the elements of the script using a simple interface. I prefer a complicated one, with more controls. Yours looks more effective.

Post a Comment

Please add 2 and 3 and type the answer here: