XAML-связывание из строки в TextBox не работает - PullRequest
0 голосов
/ 18 ноября 2011

Я новичок в C #, у которого проблемы с пониманием того, почему его первая попытка понять привязки XAML не работает. Я слежу за Microsoft Обзор привязки данных .

У меня есть один TextBox, который в конечном итоге будет служить окном состояния. Сейчас я просто хочу иметь возможность писать произвольные текстовые строки в него. У меня также есть экземпляр шаблона команды, который я тестирую. Моя команда включает в себя добавление случайного числа в аккумулятор и распечатку результата в представлении статуса.

Вот мой XAML для главного окна приложения:

<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>

И класс, представляющий мои данные:

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();
        }
    }
}

В конце концов, я буду использовать подходящий конвертер из StringBuilder, но я хотел бы разобраться с принципом, прежде чем исследовать эту сложность.

Если бы мое понимание было правильным (и, очевидно, это не так), это все ДОЛЖНО работать. Назначение привязки - это TextBox, свойство target - свойство Text. Источником привязки является свойство statusTextString класса StatusViewText. Тем не менее, когда я запускаю команду (и отлаживаю и вижу, что StatusViewText.statusTextString обновляется), TextBox не обновляется.

Я подумал, что мне, возможно, потребуется явно добавить привязку самостоятельно, поэтому я попытался добавить это после InitializeComponent() в конструкторе главного окна:

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

но это тоже не сработало.

Нужно ли запускать события PropertyChanged? Я думал, что весь смысл связующего фреймворка в том, что события запускаются и потребляются за сценой, автоматически; но может я и там ошибаюсь.

Нет явных ошибок в окне «Вывод», которые заставляют меня поверить, что мой синтаксис привязки правильный; Я просто пропускаю что-то еще.

Halp!

РЕДАКТИРОВАТЬ 13:14 EDT Спасибо mben :

Хорошо, я сделал это. Добавлено следующее:

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));
        }
    }
}

Я отладил, чтобы убедиться, что он проходил по этому пути кода, и это так. Но все равно не повезло. Есть еще мысли?

Ответы [ 3 ]

3 голосов
/ 18 ноября 2011

Вот изменение, которое вы должны применить, чтобы оно заработало.

Вот код вашей 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");
        }
    }
}

Вы должны указать DataContext вашего окна. Код позади является решением: в ctor MainWindow заполните контекст данных:

this.DataContext = new StatusViewText();

В XAML вы должны указать свою привязку к свойству StatusTextString. Это работает, потому что контекст данных является экземпляром StatusViewText.

Text="{Binding Path=StatusTextString}"
2 голосов
/ 18 ноября 2011

Вам все еще нужно реализовать INotifyPropertyChanged в вашем StatusViewText. Система привязки не собирается постоянно проверять значения, вам нужно уведомить об этом, когда все изменится.

1 голос
/ 18 ноября 2011

Обратите внимание, что вы создаете экземпляр в вашем коде, который НЕ совпадает с экземпляром, выраженным в вашем Xaml. Вы можете доказать это, установив точку останова в конструкторе StatusViewText.

Поместите этот фрагмент в конструктор вашего главного окна ...

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

Это повлияет на правильный экземпляр вашего класса.

Вы также должны изменить свой класс, чтобы он выглядел примерно так ...

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));
        }
    }
} 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...