Пользовательский элемент управления WPF с пользовательским рисунком и ActualWidth

Я пытаюсь создать собственный элемент управления (производный от Control) с собственной логикой рисования. Элемент управления просто рисует диагональную линию из верхнего левого угла в правый нижний угол элемента управления. Эта логика основана на ActualWidth и ActualHeight элемента управления, которые доступны только при отрисовке элемента управления (AFAIK).

У меня вопрос: как правильно сделать пользовательский рисунок эффективным?

Документации по этому поводу не так много, и я боюсь, что делаю что-то глупое или ненужное, например принудительное обновление/перерисовку каждый раз, когда запускается OnPropertyChanged...

Вот шаблон элемента управления:

<Style TargetType="{x:Type local:MyCustomControl}">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type local:MyCustomControl}">
        <Border Background="{TemplateBinding Background}"
                BorderBrush="{TemplateBinding BorderBrush}"
                BorderThickness="{TemplateBinding BorderThickness}">
          <Canvas>
            <Line x:Name="PART_Diagonal"
                  Y1="{Binding Path=DiagonalTop}"
                  Y2="{Binding Path=DiagonalBottom}"
                  X1="{Binding Path=DiagonalLeft}"
                  X2="{Binding Path=DiagonalRight}"
                  Stroke="Red" />
          </Canvas>
        </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

И вот логика:

public class MyCustomControl : Control, INotifyPropertyChanged
{
  static MyCustomControl()
  {
    DefaultStyleKeyProperty.OverrideMetadata( typeof( MyCustomControl ), new FrameworkPropertyMetadata( typeof( MyCustomControl ) ) );
  }

  public MyCustomControl()
  {
    DataContext = this;
  }

  protected override void OnRenderSizeChanged( SizeChangedInfo sizeInfo )
  {
    base.OnRenderSizeChanged( sizeInfo );

    var margin = 25d;

// These calculations are based on the control's current size.
    DiagonalTop = margin;
    DiagonalBottom = ActualHeight - margin;
    DiagonalLeft = margin;
    DiagonalRight = ActualWidth - margin;

// Is this forcing redraws (i.e. invalidating the layout)?
    OnPropertyChanged( "DiagonalTop" );
    OnPropertyChanged( "DiagonalBottom" );
    OnPropertyChanged( "DiagonalLeft" );
    OnPropertyChanged( "DiagonalRight" );
  }

  public double DiagonalTop { get; private set; }
  public double DiagonalBottom{ get; private set; }
  public double DiagonalLeft { get; private set; }
  public double DiagonalRight { get; private set; }

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

person Reyhn    schedule 24.05.2012    source источник


Ответы (1)


Вы могли бы сделать это намного проще, без каких-либо дополнительных свойств. Просто используйте свойство Padding для внутреннего интервала. :

<ControlTemplate TargetType="{x:Type local:MyCustomControl}">
    <Border Background="{TemplateBinding Background}" 
            BorderBrush="{TemplateBinding BorderBrush}" 
            BorderThickness="{TemplateBinding BorderThickness}"
            Padding="{TemplateBinding Padding}">
        <Canvas Name="DrawingCanvas">
            <Line X1="0" X2="{Binding ActualWidth, ElementName=DrawingCanvas}" 
                  Y1="0" Y2="{Binding ActualHeight, ElementName=DrawingCanvas}" 
                  Stroke="Red" />
        </Canvas>
    </Border>
</ControlTemplate>

Используйте свой элемент управления с Padding:

<local:MyCustomControl Padding="25" ... />
person Clemens    schedule 24.05.2012
comment
Спасибо за ваш ответ! Это хорошее предложение, но на самом деле оно не дает мне ответа, который я хочу. Меня больше интересует правильное использование OnRenderSizeChanged и обновление свойств, с которыми связаны другие элементы. - person Reyhn; 26.05.2012
comment
Хорошо, тогда вы можете повысить производительность, заменив четыре вызова OnPropertyChanged, каждый с аргументом имени свойства, одним вызовом OnPropertyChanged с нулевым аргументом или пустой строкой. Это будет означать, что все свойства изменились одновременно. - person Clemens; 26.05.2012