9

I'd like to be able to use arbitrary C# expressions in XAML. Usually this would be to compute a property for a UI element based on two bound values.

For example calculating the width of a UI element based on two other properties.

This is a contrived example of what I'd like the XAML to look like:

<TextBox
   x:Name="textBox1"
   />

<TextBox
   x:Name="textBox2"
   />

<Rectangle
    Height={Double.Parse(textBox1.Text) + Double.Parse(textBox2.Text)}
    />

Of course there is no built-in way of doing this in XAML.

I know that I could use a MultiBinding combined with a custom converter and this is usually the way I do this kind of thing. However it seems to me that it would be so much simpler to just include some C# code in the XAML and I was wondering if anyone out there had already solved this problem with a XAML extension or something else.

Ashley Davis
  • 9,896
  • 7
  • 69
  • 87
  • 2
    Woe betide anyone maintaining your work. – Jeff Yates Dec 03 '10 at 14:01
  • If the XAML compiler actually supported this it would be much easier to maintain these XAML files! This is because you wouldn't have to go creating loads of converter classes to do the work and less classes means less maintenance. – Ashley Davis Dec 03 '10 at 14:06
  • 1
    Yes, that may be so, but that defeats the object of separating presentation from business logic, which means MORE maintenance. And, because of non-standard usage, confusion. – Jeff Yates Dec 03 '10 at 14:08
  • But maybe the expression isn't business logic? – Ashley Davis Dec 03 '10 at 14:10
  • Besides there are occasions when the separation of ui and business logic is good and that is what you want. There are also times when you want to get a throw-away prototype done quickly and you simply don't care about such separation. – Ashley Davis Dec 03 '10 at 14:12

5 Answers5

2

You embed C# code into XAML like this:

 <x:Code>
            <![CDATA[

            void ButtonOnClick(object sender, RoutedEventArgs args)
            {
                Button btn = sender as Button;
                MessageBox.Show("The button labeled '" +
                                btn.Content +
                                "' has been clicked.","Information Message");
            }
            ]]>
  </x:Code>

But this approach is not recommended at all because it mixes the pure presentation layer with business logic.

Liviu Mandras
  • 6,540
  • 2
  • 41
  • 65
  • Thanks for the answer, but its not really what I am after. I want to use C# mainly to set properties of UI elements purely as a convenience to avoid having to create my own converters and use MultiBinding. – Ashley Davis Dec 03 '10 at 13:56
  • Why do you want to avoid using converters and MultiBinding? They ARE the convenience. – Jeff Yates Dec 03 '10 at 14:00
  • 1
    In what way is creating a new class more convenient than typing an expression? – Ashley Davis Dec 03 '10 at 14:07
  • 1
    The class can be independently tested – Richard Szalay Dec 03 '10 at 14:25
  • 1
    @Ashley Davis: Maintenance. Put yourself in the shoes of the poor guy who will have to maintain the app five years from now: everything is executable, everything has state, and everything depends on everything else. "But hey, the guy who wrote this said it was *so* convenient!" In other words, only 20% of time spent on the app is coding it, the remaining 80% will be maintenance. – Piskvor left the building Dec 03 '10 at 14:26
  • I know the importance of maintainability. I work in a legacy code base so have learn the lesson well. So there is no need to preach. – Ashley Davis Dec 03 '10 at 14:28
2

I've seen custom Xaml converters that take IronPython code and Invoke the DLR. It's not quite C#, but its certainly is less ugly than the approach of using [CDATA] tags.

http://pybinding.codeplex.com/

This is the link to an open source project on the matter.

Michael B
  • 7,512
  • 3
  • 31
  • 57
2

Wrap your expression into a public property and bind to that property.

In C# codebehind:

public double Heigth
{
  get { return Double.Parse(textBox1.Text) + Double.Parse(textBox2.Text); }
}

In XAML:

<Rectangle Height={Binding Heigth} />
VVS
  • 19,405
  • 5
  • 46
  • 65
  • Not really that convenient though. It would probably be easier to a create a converter and use MultiBinding than to create a new dependency property, and then handle the changed events for the two text boxes in my example to recompute the value of the expression. – Ashley Davis Dec 03 '10 at 14:24
  • Thanks for the example, but you are missing the code that calls 'PropertyChanged' for "Height" whenever textBox1.Text and textBox2.Text have changed. If you don't do this the UI won't be able to update correctly. – Ashley Davis Dec 03 '10 at 14:30
  • @Ashley Davis: You're right. It's also missing any exception handling and depends on the user to input correct values, which is never a good idea. – VVS Dec 03 '10 at 15:51
1

Please mind that with the code like

Height={Double.Parse(textBox1.Text) + Double.Parse(textBox2.Text)}

it's particularly hard (although not completely impossible, keeping Linq Expressions in mind) to get the value reevaluated as soon as some of the operands change. The automatic update of the target value when the source changes is one of the major advantages of WPF bindings.

Vlad
  • 35,022
  • 6
  • 77
  • 199
  • I wasn't trying to pretend that was valid XAML! In theory it shouldn't be too hard to have the expression re-evaluate. If the XAML compiler could actually process that expression it would know that 'textBox1.Text' and 'textBox2.Text' are dependency properties and it would therefore know that when they change it has to re-evaluate the expression. – Ashley Davis Dec 03 '10 at 14:04
  • 1
    @Ashley: neither did I. Having this in mind, the only way to implement the said functionality would be using a markup extension. The said extension could be tricky enough to build the linq expression, examine all the operand types, and build an appropriate multibinding. – Vlad Dec 03 '10 at 14:14
  • That sounds like an interesting approach. I'm might look into it sometime (if someone hasn't already given me the answer here ;) – Ashley Davis Dec 03 '10 at 14:19
0

I have an answer to my question now. It isn't the answer I was originally looking for, and it is a bit long winded but it does work.

I was reading this post by Josh Smith. He recommends not using value converters but pushing the calculations down into the view-model and exposing it as a property:

http://groups.google.com/group/wpf-disciples/browse_thread/thread/3fe270cd107f184f?pli=1

In my case the text for both 'textBox1' and 'textBox2' should be bound into the view-model, so when they change in the view-model I know its time to do the calculation and update the dependent property. The dependent property then fires its property changed event and the UI updates from that.

If you have a case where you want to make the expression depend on read-only control properties, that you can't easily bind to the view-model, you can follow the advice presented here:

Pushing read-only GUI properties back into ViewModel

I still would like to have the ability to embed (non-business logic) expressions in the XAML. But seeing as this is not built-in any way of doing it is likely to be a bit of a hack. Going through the view-model seems to be the correct way of doing this, but maybe one-day I'll experiment with writing a markup extension that allows expression in XAML.

Community
  • 1
  • 1
Ashley Davis
  • 9,896
  • 7
  • 69
  • 87