Обновить WPF UserControl через XAML

Я использую WPF в Visual Studio 2008. У меня есть простой WPF UserControl со следующим кодом:

  public partial class UserControl1 : UserControl
  {
    public UserControl1()
    {
      InitializeComponent();
      Composite = new Composite();
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
      //LayoutRoot is name of default Grid instance
      if (!LayoutRoot.Children.Contains(Composite))
      {
        LayoutRoot.Children.Add(Composite);
      }
    }

    public Composite Composite
    {
      get;
      set;
    }
  }

  public class Composite : ContentControl
  {
    protected override void OnRender(DrawingContext drawingContext)
    {
      drawingContext.DrawRectangle(new SolidColorBrush(Color), new Pen(Brushes.Black, 1.0), new Rect(RenderSize));
    }

    public Color Color
    {
      get;
      set;
    }
  }

Затем я использую этот UserControl в приложении WPF, XAML страницы выглядит следующим образом:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:test="clr-namespace:WpfControlLibrary1;assembly=WpfControlLibrary1"
    Title="Window1" Height="500" Width="700" Background="AliceBlue">
  <test:UserControl1 Name="uControl1">
    <test:UserControl1.Composite>
      <test:Composite Color="Green"/>
    </test:UserControl1.Composite>
  </test:UserControl1>
</Window>

У меня вопрос: какой код мне нужно добавить к вышеперечисленному, чтобы при изменении «Составного цвета» на что-то иное, кроме зеленого и нажатие кнопки возврата, UserControl автоматически обновлялся? Я ищу поведение, которое происходит, когда вы меняете фон Window1 на цвет, отличный от AliceBlue, и нажимаете return.

Когда я запускаю код, виден правильный цвет, проблема заключается в обновлении во время разработки через XAML.

Большое спасибо за любые указатели, которые помогают мне понять, что здесь происходит!


person Shunyata Kharg    schedule 27.05.2009    source источник


Ответы (2)


WPF оптимизирован для повторного рисования элементов только тогда, когда это абсолютно необходимо, поэтому поведение, которое вы видите, частично объясняется тем, что WPF не знает, что при изменении Composite.Color ему необходимо повторно рисовать элемент управления.

Вдобавок к этому дизайнер Visual Studio / Blend (или элементы управления XAML) не сможет заметить, что это свойство изменилось, поскольку ваш элемент управления не сигнализирует об изменении этого свойства; без этого уведомления WPF не знает, что свойство было изменено (что избавляет его от постоянной проверки на предмет изменений).

Хотя вы можете реализовать INotifyPropertyChanged и исправить вторую проблему, поскольку вы наследуете от ContentControl (который на верхнем уровне иерархии наследования реализует INotifyPropertyChanged), вы можете использовать DependencyProperty и исправить обе проблемы - и получить лучшую поддержку привязки WPF, чтобы ботинок!

// DependencyProperty backing store for Color.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty ColorProperty =
    DependencyProperty.Register("Color", typeof(Color), typeof(Composite), 
    new FrameworkPropertyMetadata(new Color(), FrameworkPropertyMetadataOptions.AffectsRender));

public Color Color
{
    get { return (Color)GetValue(ColorProperty); }
    set { SetValue(ColorProperty, value); }
}

Установив FrameworkPropertyMetadataOptions.AffectsRender, WPF знает, что при изменении этого свойства этап рендеринга для этого элемента необходимо выполнить снова, и повторно отобразит ваш элемент управления, вызвав ваш метод OnRender () и отобразив новый цвет. И, как DependencyProperty, он автоматически генерирует правильное событие PropertyChanged, так что любой, кто наблюдает за вашим классом (например, система привязки и анимации WPF), получит уведомление об обновлении, как только свойство изменится.

person Nicholas Armstrong    schedule 27.05.2009
comment
Спасибо, Николас, но этот код вызывает ошибку System.Windows.Markup.XamlParseException при применении к моему примеру кода. Если я беру параметр FrameworkPropertyMetadataOptions.AffectsRender из конструктора FrameworkPropertyMetadata, я не получаю сообщение об ошибке, но моя проблема не решена. - person Shunyata Kharg; 28.05.2009
comment
Вот что я получаю за то, что не полностью протестировал свой код! Я обновил код и проверил, что он работает - я по ошибке инициализировал DependencyProperty значением null, что не удалось с XamlParseException. Инициализируя значение белым цветом с помощью new Color (), DependencyProperty инициализируется, и конструктор обновляется, как только вы меняете цвет. Мои извенения. - person Nicholas Armstrong; 28.05.2009
comment
Теперь это работает, спасибо, Николас! Установка для параметра defaultValue значения null кажется проблемой, только если вы определяете параметр flags, в противном случае null работает нормально. Во всяком случае, теперь я могу отметить ваш ответ как ответ. Спасибо еще раз! - person Shunyata Kharg; 28.05.2009

Вы делаете это очень странным образом (напоминающим элемент управления MFC); уделите время и прочтите первую половину или около того WPF Unleashed (https://rads.stackoverflow.com/amzn/click/com/0672328917), читать приятно и во всем будет намного больше смысла.

person Ana Betts    schedule 28.05.2009
comment
Спасибо за подсказку, Пол. Элемент управления, над которым я работаю, использует большие наборы данных, которые обычно слишком велики для ввода в XAML во время разработки. Это означает, что мне нужно динамически создавать большое количество объектов во время выполнения, что вполне возможно сделать способом MFC с использованием WPF. Тем не менее, я также хотел бы представить еще несколько преимуществ WPF для этой модели, поэтому я унаследовал эти объекты от ContentControl и заставил каждый объект отображать себя, вместо того, чтобы передавать DrawingContext от одного к другому. - person Shunyata Kharg; 28.05.2009