Давайте разберемся с этим.
Что происходит
Ваш MyClass
будет иметь переопределение на ToString
, генерирует строку, которая представляет значение объекта, которое будет функцией назначенных вами свойств. Когда вы назначаете объект для Content
платформы SL, он вызывает ToString
, чтобы получить текст для отображения. Как вы правильно поняли, назначая тот же
ссылка не вызывает этот вызов снова.
Простое решение, которое поможет вам и, вероятно, достаточно хорошо
Button button = new Button();
MyClass myClass = new MyClass() { A = 1, B = 2 };
button.Tag = myClass;
button.Content = myClass.ToString();
stackPanel.Children.Add(button);
...
Button button = (Button)stackPanel.Children[0];
MyClass myClass = (MyClass)button.Tag;
myClass.A = 421;
button.Content = myClass.ToString();
Свойство Tag
фактически является колышком, на котором мы можем повесить связанный объект, который нам может понадобиться позже. В этом случае мы выполняем операцию ToString (). Hance Content всегда получает другую ссылку на то, что у него уже есть (по крайней мере, всегда, когда значения фактически различаются).
Более традиционное решение SL, но не обязательно желательно в этом случае
Чтобы достичь того, чего вы хотите, вам нужно сделать несколько вещей. Сначала вам нужно будет выставить свойство строкового типа в MyClass
, которое содержит строку, которую вы хотите представить, давайте назовем ее DisplayValue
.
public string DisplayValue { get { return String.Format("{0} {1}", A, B); } }
Теперь вам нужно каким-то образом привязать результат этого к кнопке, чтобы она могла отображать значение. Для начала следует назначить экземпляр MyClass
объекту DataContext
вместо Tag
. Однако Button
не имеет свойства Text
, где в других случаях вы бы связывали DisplayProperty
. Следовательно, вам нужно добавить TextBlock
в качестве содержимого кнопки и связать ее Text
свойство: -
Button button = new Button();
TextBlock tb = new TextBlock();
tb.SetBinding(TextBlock.TextProperty, new Binding("DisplayValue"));
button.Content = tb;
button.DataContext = myClass;
Однако для того, чтобы управление знало, что связанное значение было изменено, ваш MyClass
должен реализовать INotifyPropertyChanged
. Вы добавляете это в свой класс следующим образом: -
public MyClass : INotifyChanged
{
// ... the rest of you code
public event PropertyChangedEventHandler PropertyChanged;
}
Затем вам нужно вызывать событие всякий раз, когда свойство изменяется, типичный пример: -
private int _A
public int A
{
get { return _A; }
set
{
_A = value;
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs("A"));
}
}
Тем не менее этот типичный пример не помогает. В вашем случае вы привязались к DisplayValue
, и поскольку значение A влияет на его значение, ваш класс также должен уведомить DisplayValue
об изменении: -
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("A"));
this.PropertyChanged(this, new PropertyChangedEventArgs("DisplayValue"));
}
Как вы можете видеть, оно значительно сложнее, чем другое решение, но, похоже, больше не дает. Ключевое преимущество заключается в том, что логика в вашем приложении теперь может концентрироваться на изменяющихся значениях объекта, не заботясь о том, чтобы поддерживать отображение в актуальном состоянии, которое само по себе заботится. Следовательно, это обеспечивает хорошее отделение логического кода от кода представления.
Так почему же это не желательно? На самом деле это зависит от того, что представляет ваш реальный класс, но на самом деле DisplayValue
определяет, как должно отображаться состояние объектов на экране. Однако действительно ли эта ответственность ложится на класс или это не ответственность за код представления, как правило, последний является верным, и DisplayValue
, как правило, не очень хорошая идея (и это причина, по которой немногие классы действительно беспокоятся о переопределении ToString
) .