Thursday, May 31, 2007

Fully binded validation by using Dependency and Attached properties

Yesterday, I got a request from one of my clients to create validation rule with range, binded to source or any other data. Well, that's easy - I though. However, there are a couple of tricks, you should know, to perform it.

Let's start from the beginning. Custom validation rule have to inherit from ValidationRule class, this means, that it can not be dependency object. But how said, that it can not have dependency object member? It can. So, first of all we need a validation rule.

    public class MinMaxValidationRule:ValidationRule
{
private Int32RangeChecker validRange;

public Int32RangeChecker ValidRange
{
get { return validRange; }
set { validRange = value; }
}

public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
int res = int.MinValue;
bool isNumber = int.TryParse(value.ToString(), out res);
bool isValidRange = true;
if (validRange == null)
{
isValidRange =
true;
}
else
{
isValidRange = (res > validRange.Minimum & res < validRange.Maximum);
}
string errorString = !isNumber ? "The string is in incorrect format" : !isValidRange ? "The input integer is out of range" : string.Empty;
return new ValidationResult(isNumber && isValidRange, errorString);

}
}

 


Well, I have not explain what is doing, right? :) Next we'll create DependencyObject Int32RangeChecker. That's simple as well.


    public class Int32RangeChecker : DependencyObject
{
public int Minimum
{
get { return (int)GetValue(MinimumProperty); }
set { SetValue(MinimumProperty, value); }
}
public static readonly DependencyProperty MinimumProperty =
DependencyProperty.Register("Minimum", typeof(int), typeof(Int32RangeChecker), new UIPropertyMetadata(int.MinValue));


public int Maximum
{
get { return (int)GetValue(MaximumProperty); }
set { SetValue(MaximumProperty, value); }
}
public static readonly DependencyProperty MaximumProperty =
DependencyProperty.Register("Maximum", typeof(int), typeof(Int32RangeChecker), new UIPropertyMetadata(int.MaxValue));

}

 


Now, let's XAML it. I'm using converter-validator explained earlier. You really do not have to do it.


    <TextBox>
<
TextBox.Text>
<
Binding Path="[1]" UpdateSourceTrigger="PropertyChanged">
<
Binding.Converter>
<
local:MyInt32Converter xmlns:local="clr-namespace:BindedValidator" />
</
Binding.Converter>
<
Binding.ValidationRules>
<
local:MinMaxValidationRule/>
<
local:MinMaxValidationRule.ValidRange>
<local:Int32RangeChecker
Minimum="{Binding Source={StaticResource dataSource}, Path=MinValue}"
Maximum="{Binding Source={StaticResource dataSource}, Path=MaxValue}"/>
</local:MinMaxValidationRule.ValidRange>
</local:MinMaxValidationRule
>
</
Binding.ValidationRules>
</
Binding>
</
TextBox.Text>
</
TextBox>

 


There are a couple of tricks here. First, I'm using explicitly set UpdateSourceTrigger. This one, will force the validation on source change, rather then on focus lost. The other trick, is that if you even have data context, or you want to set control as data source for internal DP - this will not work. Why? 'cos our dependency object is not part of logical tree, so you can not use ElementName or DataContext as source for internal data binding.


So far, so good. What to do if I want to bind to control (e.g. slider) in my page? You have to put local:Int32RangeChecker DependencyObject in your resources and use it following way.


<Slider Minimum="0" Maximum="20" Value="{Binding Source={StaticResource rangeConverter}, Path=Minimum, Mode=OneWayToSource}" Name="minVal"/>
<
Slider Minimum="0" Maximum="20" Value="{Binding Source={StaticResource rangeConverter}, Path=Maximum, Mode=OneWayToSource}" Name="maxVal"/>
<
TextBox>
<
TextBox.Text>
<
Binding Path="[1]" UpdateSourceTrigger="PropertyChanged">
<
Binding.Converter>
<
local:MyInt32Converter xmlns:local="clr-namespace:TreeViewItemEdit" />
</
Binding.Converter>
<
Binding.ValidationRules>
<
local:MinMaxValidationRule ValidRange="{StaticResource rangeConverter}"/>
</Binding.ValidationRules>
</
Binding>
</
TextBox.Text>
</
TextBox>

 


Well. That's one of methods to do data binding inside validation rule. The other approach is by using Attached Properties. You can create static class with properties, can be attached to your validation range source. The XAML will looks like this


<TextBox 
l:MinMaxValidator.Minimum="{Binding Source={StaticResource rangeConverter}, Path=Minimum}"
l:MinMaxValidator.Maximum="{Binding Source={StaticResource rangeConverter}, Path=Maximum}">
<
TextBox.Text>
<
Binding Path="[1]" UpdateSourceTrigger="PropertyChanged">
<
Binding.Converter>
<
local:MyInt32Converter xmlns:local="clr-namespace:TreeViewItemEdit" />
</
Binding.Converter>
</
Binding>
</
TextBox.Text>
</
TextBox>

 


That's looks much better, isn't it? But how to evaluate validation, while you are in completely different class. How to know where to do it and create "on-the-fly" binding from code. Look here


    public static class MinMaxValidator
{
public static int GetMinimum(DependencyObject obj)
{
return (int)obj.GetValue(MinimumProperty);
}

public static void SetMinimum(DependencyObject obj, int value)
{
obj.SetValue(MinimumProperty, value);
}

public static readonly DependencyProperty MinimumProperty =
DependencyProperty.RegisterAttached("Minimum", typeof(int), typeof(MinMaxValidator), new UIPropertyMetadata(int.MinValue,OnAttachedPropertyChanged));

public static int GetMaximum(DependencyObject obj)
{
return (int)obj.GetValue(MaximumProperty);
}

public static void SetMaximum(DependencyObject obj, int value)
{
obj.SetValue(MaximumProperty, value);
}

public static readonly DependencyProperty MaximumProperty =
DependencyProperty.RegisterAttached("Maximum", typeof(int), typeof(MinMaxValidator), new UIPropertyMetadata(int.MaxValue,OnAttachedPropertyChanged));


static void OnAttachedPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
TextBox tb = obj as TextBox;
if (tb != null)
{
if (!tb.IsInitialized)
{
EventHandler callback = null;
callback =
delegate
{
tb.Initialized -= callback;
Validate(tb);
};
tb.Initialized += callback;
}
}
}

static void Validate(TextBox tb)
{
BindingExpression exp = tb.GetBindingExpression(TextBox.TextProperty);
if (exp != null && exp.ParentBinding != null)
{
MinMaxValidationRule myRule = null;
foreach (ValidationRule rule in exp.ParentBinding.ValidationRules)
{
if (rule != null && rule is MinMaxValidationRule)
{
myRule = rule
as MinMaxValidationRule;
}
}
if (myRule == null)
{
myRule =
new MinMaxValidationRule();
exp.ParentBinding.ValidationRules.Add(myRule);
}

myRule.ValidRange =
new Int32RangeChecker();
Binding minBinding = new Binding();
minBinding.Source = tb;
minBinding.Path =
new PropertyPath(MinMaxValidator.MinimumProperty);

myRule.ValidRange.Maximum = (
int)tb.GetValue(MinMaxValidator.MaximumProperty);
myRule.Validate(tb.Text, System.Globalization.
CultureInfo.CurrentCulture);
}
}
}

 


Looks scary, don't it? No fear. That's really simple. The only place to look deeper is Validate and OnPropertyChange method. What we're doing there?


First of all, we have to be sure, that the property attached to textbox. Then we should check if the textbox initialized. Actually, binding and property attachment occures before initialization, so we have to subscribe to initialized event and run our code there. What we are doing in Validate method?


The target is get updated values and assign to the validation. It can be done by databinding or by regular setter. Now, let's take a binding expression from the data source, my properties were attached to. Next, we have to tear it's parent binding and find the validatorule from validator rules collection, that we need. If this one not found, we'll just create new and assign values to its range.


That's all, folks. We can bind range (or any other properties) of our validator and be quiet about triggers, data freshments and other nasty things, we suffer before Windows Presentation Foundation.

Three new betas from Live crew

Today released three new betas for Live environment.

Windows Messenger goes 8.5. UI leaked earlier this week, and there is no a lot of changes. But it has some new functions. Download >>

My lovely Windows Live Writer finally goes beta 2. A lot of changes. I'm using a number of months and it's really great application. No support for hebrew or russian in current version. Download >>

Windows Live Mail goes new beta. Free mail client, can easily replace Outlook express and Windows Mail. Download >>

Great thank to developer team - great work

Tuesday, May 29, 2007

WPF + UISpy = Accessibility testing

Well, some of Doron's scripts make commenting to this post(hebrew) disabled, however today I want to write about WPF accessibility issues. Someone who writes code really know what is it? I'm pretty sure, that do not. Let's make small test case. Execute regular WinForms application and Accessibility Speech engine (you can find it under Start->Accessories->Ease of Access->Narrator). Try to open calculator and go to application menu. You'll hear something like: "Menu Item Edit. Three menu items. Copy Menu Item, shorkey Control C". Where it comes from. Does narrator understand every control? How to know or control what should say? Open SpyUI from Windows SDK. Find "Calculator" node in elements tree and expand it. You'll find everything that Narrator speaking. This discovered by windows API in regular win forms.

image

Well. Now let's create simple WPF application with menu bar, button and listbox and let UISpy detect it.

image

Well, it works. But not so good. "Edit" menu item has Copy menuitem inside, but UISpy do not know about it. It's detects the containment of listbox, so it works until we have no data binded. Let's bind and see what we have.

image

Please see, that instead of what we actually see, there are System.Xml.XmlElement items are displayed. Actually, UISpy is right. We are using data, that WPF engine renders into UI. But we can not leave it this way. What to do? Make it accessible. Here the way.

First of all, we have to set TextSearch.TextPath property to our actual text. Next, override ItemContainerStyle to set its AutomationProperties.Name value to be binded to the source of text, we want to make accessible. Here the code.

    <ListBox ItemTemplate="{StaticResource template}" 
DataContext="{StaticResource data}" ItemsSource="{Binding}"
TextSearch.TextPath="@value">
<
ListBox.ItemContainerStyle>
<
Style>
<
Setter Property="AutomationProperties.Name" Value="{Binding XPath=@value}"/>
</
Style>
</
ListBox.ItemContainerStyle>
</
ListBox>

 


If you'll bind Automation Name property inside data template it will not work, 'cos for real WPF engine do not render text objects, so the only way to do it is by overriding those properties externally. How we can tell? Right property in wrong place :)


And here the result of such approach in UISpy. Now you can run Narrator or any other accessibility application and everything will work fine.


image


The conclusions are as following:


1) UISpy is extremely important tool for accessibility check
2) WPF is not very friendly from this point of view in current version, but I'm sure, that next release will fix those issues.

Monday, May 28, 2007

WPF binding to WCF and more

Windows foundations are great. But how to make them work together. Today, I decided to explain how to build simple WPF application, with data, achieved from WCF OperationContracts (Methods) with simple and complicated parameters. So, let's start.

First of all, we'll create simple WPF application, then we'll add WCF Service Library to our solution. It'll create nice template with explanations what to put where. So, we'll use it AS-IS and use our WPF application as host for WCF service (this can run in different application as well). So, do everything written in the template. Create App.config and write where

<system.serviceModel>
<
services>
<
service name="FlickrHost.service1">
<
endpoint contract="FlickrHost.IService1" binding="wsHttpBinding" />
</
service>
</
services>
</
system.serviceModel>

 


Additional file with host start and stop and methods into Window1.xaml.cs


public Window1()
{
InitializeComponent();
MyServiceHost.StartService();

}


~Window1()
{
MyServiceHost.StopService();
}

 


So far, so good. Compile, run - ERROR, AddressAccessDeniedException: "HTTP could not register URL http://+". What the hell? We can not create entrypoint as user because of UAC in Vista. So, let's see what we can do.


Open command prompt and type "netsh http show urlacl". You'll get something like this



Reserved URL            : http://+:80/wsman/
    User: NT AUTHORITY\NETWORK SERVICE
        Listen: Yes
        Delegate: Yes
        SDDL: D:(A;;GA;;;NS)

Reserved URL            : http://+:80/Temporary_Listen_Addresses/
    User: \Everyone
        Listen: Yes
        Delegate: No
        SDDL: D:(A;;GX;;;WD)


What is it? Why we can not use port 8080, provided by template. Let's add it into our account.
netsh http add urlacl url=http://+:8080/ user=DOMAIN\username. You'll get error:



Url reservation add failed, Error: 5
The requested operation requires elevation.


Run the command shell "As Administrator" and you'll success. So, UAC good or not for jews? You choose :)


Next step is creation of  ObjectDataProvider, that uses the service methods to provide results. We'll add additional method to default WCF service template, that receives no parameters. Something like this will definitely fine.


[ServiceContract()]
public interface IService1
{
[
OperationContract]
string MyOperation2(string myValue);
[
OperationContract]
string MyOperation3(DataContract1 dataContractValue);
[
OperationContract]
string MyOperation1();
}

public class service1 : IService1
{
public string MyOperation2(string myValue)
{
return "Hello: " + myValue;
}
public string MyOperation3(DataContract1 dataContractValue)
{
return "Hello: " + dataContractValue.FirstName;
}
public string MyOperation1()
{
return "Hello World!";
}
}

 


Now, back to WPF. I believe you know, that you can bind to methods?


<ObjectDataProvider ObjectType="c:service1" x:Key="operation1" MethodName="MyOperation1"/>

 


Adding TextBlock, binded to this object and wow, we have Hello World application.


<TextBlock Text="{Binding Source={StaticResource operation1}}"/>

 


So far so good. Now, we have two additional methods, that need parameters. One is regular string, but other is DataContract from WFC service. How to do it? How to understand how to pass parameters into methods. For WPF - this is really simple. For us - not so straight forward - there is no intellisense support in XAML schema for such features in VS2005 with .NET 3.0 toolkit. What we have to do, is to use ObjectDataProvider.MethodParameters variable to set parameters for the method, bided into ObjectDataProvider. Let's rock'n'roll.


<ObjectDataProvider ObjectType="c:service1" x:Key="operation2" MethodName="MyOperation2">
<
ObjectDataProvider.MethodParameters>
<
s:String>aaa</s:String>
</
ObjectDataProvider.MethodParameters>
</
ObjectDataProvider>

 


Well. We have System mscorlib clr namespace, but what to do with object (Data Contact), defined in WCF service? For real? The same stuff. The only difference is other namespace and internal object initialization and assignment. Here comes the king.


<ObjectDataProvider ObjectType="c:service1" x:Key="operation3" MethodName="MyOperation3">
<
ObjectDataProvider.MethodParameters>
<
c:DataContract1>
<
c:DataContract1.FirstName>
<
s:String>bbb</s:String>
</
c:DataContract1.FirstName>
</
c:DataContract1>
</
ObjectDataProvider.MethodParameters>
</
ObjectDataProvider>

 


So far, we have three methods, binded to objects, achieved from WCF service provider, run by WCF data host. But wait. The data is not read only. We can change it. What's the hell, we can not notify to the object data provider about changes, there are no INotifyUpdate interface implemented as well as nothing can call us back about the change. What to do?


The simpler answer - DIY. The complicated make your client application notify about. So, I'll add TextBox and Button. Once the button clicked, I'll read information from the textbox, update my WCF service and notify about the changes.


void onClick(object sender, RoutedEventArgs e)
{
ObjectDataProvider odp2 = Resources["operation2"] as ObjectDataProvider;
odp2.MethodParameters[0] = name.Text;
odp2.Refresh();

ObjectDataProvider odp3 = Resources["operation3"] as ObjectDataProvider;
((FlickrHost.
DataContract1)odp3.MethodParameters[0]).FirstName = name.Text;
odp3.Refresh();
}

 


Well. We did it. Now let's write real world application. Read the attached source name and you'll may understand, that I'm going to write service, that reads, parses and uses beautiful images, found here. But, not today :)


Source code for this article

Thursday, May 24, 2007

2D controls in 3D world - how to create "A wheel of fortune with WPF"

Before reading this post you should know what is geometry and trigonometry. As well as you should know, that in WPF you can "brush" 3D meshes with images or, even other XAML vector controls, inherit and create custom controls, use animations, dependency and attached properties and handle special events. Here the result. Now - how to do it

image

First of all, we have to create custom control to handle each wheel of slot. It must consists of cylinder or "tire", that will handle our images (or vectors). Additional "feature" is flexibility of the roll size, so we'll inherit Viewbox - the best choice for control, that do not really knows, how big it will be, but has something of certain size inside it.

Next we'll create our mesh - don't even enter with hand to this stuff - use any of well-knows editors or ready made meshes. Blender (this is not Expression Blend) - is good freeware choice with option to export rather good XAML geometry.

After this step we'll have to draw slots itself. Use Expression Design for it. You'll draw XAML - then you can port it into images if you'd like to.

Fine. We have 3d mesh and vector XAML slots to draw over the mesh. How to do it? Simple. Here nice example of usage my resource loader.

We'll need such control (3d mesh with images over) a lot, so it makes sense to create method for its creation. Also we'll have it static and it not going to change across the application life cycle, so preload, save and use - do it.

internal static FrameworkElement getSlotXAML(int index)
{
switch (index)
{
case 3: return loadResource<Canvas>("Resources/bar1.xaml"); break;
case 4: return loadResource<Canvas>("Resources/bar2.xaml"); break;
case 5: return loadResource<Canvas>("Resources/bar3.xaml"); break;
case 2: return loadResource<Canvas>("Resources/bell.xaml"); break;
case 1: return loadResource<Canvas>("Resources/limon.xaml"); break;
case 6: return loadResource<Canvas>("Resources/seven.xaml"); break;
case 0: return loadResource<Canvas>("Resources/sherry.xaml"); break;
}
return null;
}

 


Then add it to panel, that wraps cylinder.


StackPanel p = new StackPanel();

p.Orientation =
Orientation.Horizontal;

for (int i = 0; i < _cnvs.Length; i++)
{
FrameworkElement fe = getSlotXAML(i);
fe.Width = 48;
fe.Height = 48;
RotateTransform rt = new RotateTransform(-90);
rt.CenterX = fe.Width / 2;
rt.CenterY = fe.Height / 2;
fe.RenderTransform = rt;

p.Children.Add(fe
/*_cnvs[i]*/);
}

 


Now, load the cylinder itself and brush it with this panel


GeometryModel3D model = loadResource<GeometryModel3D>("Resources/Cylinder.xaml");
((DiffuseMaterial)model.Material).Brush = new VisualBrush(p);

 


Now, put in into viewport and turn the lights and the camera on. The show begins. 


ModelVisual3D visual = new ModelVisual3D();
visual.Content = model;

Viewport3D port = new Viewport3D();
port.Children.Add(visual);
PerspectiveCamera camera = new PerspectiveCamera(new Point3D(0, 0, 1.7), new Vector3D(0, 0, -1), new Vector3D(0, 1, 0), 45);
port.Camera = camera;

ModelVisual3D lights = new ModelVisual3D();
lights.Content =
new AmbientLight(Colors.White);
port.Children.Add(lights);

 


Fine. We have static wheel with XAML vectors over it. Let's create a movie. Very important to accelerate and decelerate the spinning speed in order to make a feel of "fear play", so we'll recreate our animation with factor of it each couple of seconds. Turn timer and do following on tick.


if (i == 10)
i = 1;
else
i++;
_animation =
new DoubleAnimation(0, 360, new Duration(TimeSpan.FromSeconds(2/i)));
_animation.RepeatBehavior =
RepeatBehavior.Forever;
((
RotateTransform3D)((GeometryModel3D)((ModelVisual3D)((Viewport3D)_element).Children[0]).Content).Transform).Rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, _animation);

 


What's the hell of this casting? For real? My laziness - do it better. All we have to do it apply rotation transform on our "wheel" - that's all. Now, upon the animation stop increase or decrease its speed ratio. Remember - don't too fast - cops with radars are around there and not too slow - user will fall in sleep.


if (!_ftime && _speed > 2000)
{

_speed = 2000;
_goesDown = -1;
_coef = 200;
_ftime =
true;
_stopRequested =
true;


}
if (_speed > 1000)
{
_coef = 500;
_ftime =
false;
}
else if (_speed > 500 & _speed <= 1000)
_coef = 300;
else if (_speed > 300 & _speed <= 500)
_coef = 100;
else if (_speed > 100 & _speed <= 300)
_coef = 50;
else if (_speed < 100)
_coef = 5;

if (_speed >= 2000)
{
_goesDown = -1;
_speed = 2000;
}

if (_speed <= 2000)
{
_speed += _goesDown * _coef;
}

if (_speed <= 0)
{
_speed += _coef;
_goesDown = 1;
}



_animation.Duration =
new Duration(TimeSpan.FromMilliseconds(_speed));
_animation.From = 0;

((RotateTransform3D)((GeometryModel3D)((ModelVisual3D)((Viewport3D)_element).Children[0]).Content).Transform).Rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, _animation);

 


Now, we'll have to stop it over random position, BUT the position should show full tile, so calculate it.


if (_stopRequested)
{
_stopRequested =
false;
_animation.Completed -=
new EventHandler(_animation_Completed);
_animation.Completed +=
new EventHandler(_animation_Win);

_win = _rnd.Next(_cnvs.Length);


_animation.To = (360 / _cnvs.Length * _win) - _animation.From;
((RotateTransform3D)((GeometryModel3D)((ModelVisual3D)((Viewport3D)_element).Children[0]).Content).Transform).Rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, _animation);

return;
}

 


To notify our future owner, you should have two read only dependency objects - one for the item (kind of enum) and the second is for item (if someone want to show it in order place). Cloning is not a big strength of WPF, so we'll have to read our tile once again.


        public static readonly DependencyPropertyKey WinningNumberPropertyKey = DependencyProperty.RegisterReadOnly("WinningNumber", typeof(int), typeof(RollSlot), new UIPropertyMetadata(0));

static DependencyProperty WinningNumberProperty = WinningNumberPropertyKey.DependencyProperty;

public int WinningNumber
{
get { return (int)GetValue(WinningNumberProperty); }
}

public static readonly DependencyPropertyKey WinningItemPropertyKey = DependencyProperty.RegisterReadOnly("WinningItem", typeof(FrameworkElement), typeof(RollSlot), new UIPropertyMetadata(null));

static DependencyProperty WinningItemProperty = WinningItemPropertyKey.DependencyProperty;

public FrameworkElement WinningItem
{
get { return (FrameworkElement)GetValue(WinningItemProperty); }
}

void _animation_Win(object sender, EventArgs e)
{

SetValue(WinningNumberPropertyKey, (
int)_win);
SetValue(WinningItemPropertyKey, GetSlotXAML((
Slots)_win));

}

 


We done. The only think we should do is initialize slots and put them into our new parent.


<StackPanel Orientation="Horizontal">
<
l:RollSlot Width="300" x:Name="slot1" MouseDown="reinit"/>
<
l:RollSlot Width="300" x:Name="slot2" MouseDown="reinit"/>
<
l:RollSlot Width="300" x:Name="slot3" MouseDown="reinit"/>
</
StackPanel>

 


That's all, folks. Source code is attahced - it's dirty 'cos I'm really lazy to clean it up, so use it as is (top secret - it consist of some very useful "dead code", that wrote while testing solutions. Those can be real helpers for your job). Ah, one other thing don't forget to give me a credit and write something about me.


Source code for this article.

Amazing speech recognition

Yochay is going to speak at Tech-Ed Orlando about speech recognition in Windows Vista. Today I saw extraordinary video about nice try to write Perl script by using this engine. It's very hilarious, really!  See yourself.

Yochay, good luck - you'll need it :)

Sunday, May 20, 2007

Create editable TreeViewItem using styles

One of clients asked me a question: "How easily create editable TreeViewItem". I answers: "Simple. Just use styles, triggers and templates." Let's start. We need something like this.

image

First of all, let's create simple tree with data, provided by XmlDataSource.  Something like this will be good enough

<root>
<
leaf id="1" name="leaf1">
<
group id="1" name="group1">
<
item name="item1" id="1">
<
subitem id="1">test 1</subitem>
<
subitem id="5">test 2</subitem>
<
subitem id="5">test 3</subitem>
<
subitem id="5">test 4</subitem>
<
subitem id="5">test 5</subitem>
</
item>

 


Next step is to create HierarchicalDataTemplate. Just for fun, let's do it dummy way :)


<HierarchicalDataTemplate DataType="leaf" ItemsSource ="{Binding XPath=*}">
<
TextBlock Text="{Binding XPath=@name}" />
</
HierarchicalDataTemplate>
<
HierarchicalDataTemplate DataType="group" ItemsSource ="{Binding XPath=*}">
<
TextBlock Text="{Binding XPath=@name}" Foreground="Blue" />
</
HierarchicalDataTemplate>
<
HierarchicalDataTemplate DataType="item" ItemsSource ="{Binding XPath=*}">
<
TextBlock Text="{Binding XPath=@name}" Foreground="Red"/>
</
HierarchicalDataTemplate>
</
Window.Resources>

 


Next, I want to replace TextBlock inside node with editable TextBox, but only when I have no items inside. It's possible to do it "smart way", by creating ControlTemplates of HeaderedItemControl, that looks if it has items inside and choose wherever it need to choose, But the original question was "how to do it simple way". So, we'll create data template for it


<DataTemplate x:Key="editableName">
<
TextBox Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=HeaderedItemsControl}, Path=(Header).InnerText}"/>
</
DataTemplate>

 


How, what we have here? In order to show current data inside the TextBox text, we have to bind it to the ancestor's header. Actually it's XmlElement in our case, to we have to get InnerText property of the node. Simple - isn't it? BTW, you can find Ancestor type of TreeViewItem - result will be exactly the same.


The last step - create a style, that has trigger for IsSelected property of TreeViewItem and change a template. Really, simple


<Style TargetType="TreeViewItem">
<
Style.Triggers>
<
Trigger Property="IsSelected" Value="True">
<
Setter Property="HeaderTemplate" Value="{StaticResource editableName}" />
</
Trigger>
</
Style.Triggers>
</
Style>

 


Now, I have a homework for you - create the same thing for those TreeViewItems, which has Items inside them. This is the clue, that might help you.


image


Source code for this article

Friday, May 18, 2007

Microsoft Popfly has been announced

Today, Microsoft announced new cool tool named Popfly. What is it? Let's take The Information, make it easily accessed, and able users to build Their Own Information with Silverlight. What we got? New exciting tool of Wow.

Express Yourself

What we can do with Popfly? With Popfly everyone can build and share mash-ups, gadgets, web pages - other words Web Blocks. How to build them? Use online tutorials, professional and non-professional community. Who the target audience? Everyone, really. Everyone can build things with this simple, yet smart engine. Developers can use Addin for VS, if they do not feel good with this simple online engine :) How to make the world know? Look here

Another Wow? Probably...

Want to know more? Take a look here. After it, read dev team blog. And then, if you already do not read it, subscribe to somasegar, scottgu and johnmont - they know a lot about it :)

Wednesday, May 16, 2007

StaticResource, DynamicResource, x:Static - what's all this about?

So, you know to write very complicated things in XAML code, but, really, do you know what's the difference between static and dynamic resource? Do you know what's really x:Static means? We'll try today to understand differences and things we can do with this stuff. All those are markup extensions. Let's start from the very beginning - MSDN

A markup extension can be implemented to provide values for properties in attribute syntax, properties in property element syntax, or both.

StaticResource - Provides a value for a XAML property by substituting the value of an already defined resource

DynamicResource - Provides a value for a XAML property by deferring that value to be a runtime reference to a resource. A dynamic resource reference forces a new lookup each time that such a resource is accessed.

I can not tell more clear then this statement. If you want to reuse something already exists - use StaticResource, if it's changing - DynamicResource. So StaticResource is restrictive resource, when DynamicResource is not. So, other words, if you need only properties to be changed, use StaticResource, if whole object changing - use DynamicResource . The best example for this is following code

      <TextBlock Text="This is Active caption color" Background="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
<
TextBlock Text="This is not Active caption color" Background="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}">
<
TextBlock.Resources>
<
SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Yellow" />
</
TextBlock.Resources>
</
TextBlock>
<
TextBlock Text="This is Active caption color" Background="{StaticResource {x:Static SystemColors.HighlightBrushKey}}"/>
<
TextBlock Text="This is also Active caption color" Background="{StaticResource {x:Static SystemColors.HighlightBrushKey}}">
<
TextBlock.Resources>
<
SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Yellow" />
</
TextBlock.Resources>
</
TextBlock>

 


The result is following


image


As you can see, I completely change HighlightBrush object by using local resources. This works if the resource set as dynamic, but wont if static.


That's the reason, that you can not bind to DynamicResource, only to StaticResource properties or method results. Data binding engine can not be in peace with changing objects.


So, what is x:Static? It's XAML-defined Markup extension. There are some of them x:Type, x:Static x:Null and x:Array.



x:Static - Produces static values from value-type code entities that are not directly the type of a property's value, but can be evaluated to that type


Other words - the exit point for your static methods, properties and singletons. Why there is not x:Dynamic? Because everything else is dynamic in WPF. So, if you have singleton or static properties and method results, you want to bind to - use this extension.