0

I'm a C# newbie who's having problems understanding why his first attempt at understanding XAML bindings isn't working. I'm following Microsoft's Data Binding Overview.

I have a single TextBox which will eventually serve as a status window. For now I just want to be able to write arbitrary text strings into it. I also have an instance of the command pattern that I'm testing. My command involves adding a random number to an accumulator and printing out the result to the status view.

Here's my XAML for the main application window:

<Window x:Class="Experiment.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:c="clr-namespace:Experiment"
        Title="Test" Height="500" Width="700" Name="Test" Closing="Test_Closing" DataContext="{Binding ElementName=statusText}" xmlns:my="clr-namespace:Experiment">
    <Window.Resources>
        <c:StatusViewText x:Key="statusViewText" />
    </Window.Resources>
    <DockPanel HorizontalAlignment="Stretch" Margin="0,0,0,0" Width="Auto">
        <!-- Elements deleted for brevity. -->
        <TextBox Margin="5,5,5,5" Name="statusText" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" AcceptsReturn="True" AcceptsTab="True" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" FontFamily="Courier New" FontSize="12"
                 Text="{Binding Source={StaticResource statusViewText}, Path=statusTextString, Mode=OneWay}"/>
    </DockPanel>
</Window>

And the class representing my data:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Experiment {
    public class StatusViewText {
        public StringBuilder statusText { get; set; }
        public String statusTextString { get; set; }

        public StatusViewText() {
            statusText = new StringBuilder();
        }

        public void Append(string s) {
            if (s != null) {
                statusText.Append(s);
            }

            statusTextString = statusText.ToString();
        }

        public void AppendLine(string s) {
            if (s != null) {
                statusText.AppendLine(s);
            }

            statusTextString = statusText.ToString();
        }
    }
}

Eventually I'll use a proper converter here from StringBuilder, but I wanted to get the principle down before exploring that complexity.

If my understanding were correct (and obviously it isn't), this all SHOULD work. The binding target is the TextBox, target property is the Text property. The binding source is the StatusViewText class's statusTextString property. Yet when I run the command (and debug and see StatusViewText.statusTextString being updated), the TextBox doesn't update.

I thought that I may need to explicitly add the binding myself, so I tried adding this after InitializeComponent() in the main window constructor:

        statusViewText = new StatusViewText();
        Binding statusViewTextBinding = new Binding("statusTextString");
        statusViewTextBinding.Source = statusViewText;
        statusText.SetBinding(TextBlock.TextProperty, statusViewTextBinding);

but that didn't work either.

Do I need to fire PropertyChanged events? I thought the entire point of the binding framework is that events are fired and consumed behind the scenes, automagically; but maybe I'm wrong there too.

No obvious errors are coming up in the Output window which leads me to believe that my binding syntax is correct; I'm just missing something else.

Halp!

EDIT 13:14 EDT Thanks mben:

Ok, I did that. Added the following:

public class StatusViewText : INotifyPropertyChanged {
    public void Append(string s) {
        if (s != null) {
            statusText.Append(s);
        }

        statusTextString = statusText.ToString();
        NotifyPropertyChanged("statusTextString");
    }

    public void AppendLine(string s) {
        if (s != null) {
            statusText.AppendLine(s);
        }

        statusTextString = statusText.ToString();
        NotifyPropertyChanged("statusTextString");
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(String info) {
        if (PropertyChanged != null) {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}

I debugged to verify that it was going through this code path, and it is. But still no luck. Any other thoughts?

3
  • Try using DynamicResource in place of StaticResource Commented Nov 18, 2011 at 18:04
  • @PankajUpadhyay: Doesn't compile. And isn't the idea of "Resource" here effectively a pointer? I mean, I always will want this TextBox to point to the same property. That will never change, so I should be able to set that at compile-time. Commented Nov 18, 2011 at 18:23
  • Change TextBlock to TextBox in your binding, and delete all the binding from the XAML Commented Nov 18, 2011 at 18:45

3 Answers 3

3

Here are the change you have to apply to have it working.

Here's the code of your ViewModel:

public class StatusViewText : INotifyPropertyChanged
{
    public StatusViewText()
    {
        // At least, I have a default value
        this.StatusTextString = "Hello world";
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    private string statusTextString;
    public string StatusTextString
    {
        get { return this.statusTextString; }
        set
        {
            this.statusTextString = value;
            this.OnPropertyChanged("StatusTextString");
        }
    }
}

You have to specify the DataContext of your window. Code behind is a solution: in the ctor of MainWindow, fill the data context:

this.DataContext = new StatusViewText();

In the XAML, you should say your binding on a property StatusTextString. It works because the data context is an instance of StatusViewText.

Text="{Binding Path=StatusTextString}"
Sign up to request clarification or add additional context in comments.

Comments

2

You still need to implement the INotifyPropertyChanged on your StatusViewText. The binding system is not going to check the values continuously, you need to notify it when things change.

2 Comments

This is working : statusText.SetBinding(TextBox.TextProperty, statusViewTextBinding); (TextBox instead of TextBlock) and I deleted all the binding from the XAML ( I have the code working if you want me to post it.
Perfect... it was TextBox versus TextBlock. Thanks so much for your help!
1

Take note that you are creating an instance in your code which is NOT the same instance expressed in your Xaml. You can prove this by setting a breakpoint in the constructor of StatusViewText.

Place this fragment into your MainWindow's constructor...

        StatusViewText o = (StatusViewText)FindResource("statusViewText") as StatusViewText;
        if(o!=null)
        {
            o.Append("hello");   
        }

This will affect the correct instance of your class.

You should also change your class to look something like this...

public  class StatusViewText:INotifyPropertyChanged
{
    public StringBuilder statusText { get; set; }
    private string _statusTextString;
    public String statusTextString
    {
        get { return _statusTextString; }
        set 
        { 
            _statusTextString = value; 
            OnPropertyChanged("statusTextString");
        }
    }
    public StatusViewText()
    {
        statusText = new StringBuilder();
    }
    public void Append(string s)
    {
        if (s != null)
        {
            statusText.Append(s);
        }
        statusTextString = statusText.ToString();
    }
    public void AppendLine(string s)
    {
        if (s != null)
        {
            statusText.AppendLine(s);
        }
        statusTextString = statusText.ToString();
    }
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string prop)
    {
        if(PropertyChanged!=null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }
    }
} 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.