2014-01-26 4 views
4

Я использую WPF КАРТОГРАФИРОВАНИЯ ToolKit через пространство имен:Как привязать к другому элементу управления собственности в WPF

xmlns:ChartingToolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit" 
xmlns:VisualizationToolkit="clr-namespace:System.Windows.Controls.DataVisualization;assembly=System.Windows.Controls.DataVisualization.Toolkit" 

У меня есть элемент управления диаграммой, в которой я генерировать случайный цвет для каждого времени предпринят Lineseries участок , Я удалить маркеры точек данных и раскрасить, используя следующий стиль

<Style x:Key="LineDataPointStyle" 
     TargetType="ChartingToolkit:LineDataPoint"> 
    <Setter Property="IsTabStop" Value="False"/> 
    <Setter Property="Width" Value="NaN"/> 
    <Setter Property="Height" Value="NaN"/> 
    <Setter Property="Background" 
      Value="{Binding RelativeSource={RelativeSource Self}, 
          Converter={StaticResource ColorBrushConverter}}"/> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="ChartingToolkit:LineDataPoint"> 
      <Grid x:Name="Root" Opacity="0"/> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

В LineSeries определяется с помощью

<ChartingToolkit:LineSeries Title="{Binding DisplayName}" 
          AnimationSequence="FirstToLast" 
          SnapsToDevicePixels="True" 
          DataPointStyle="{StaticResource LineDataPointStyle}" 
          ItemsSource="{Binding Data}" 
          IndependentValueBinding="{Binding X}" 
          DependentValueBinding="{Binding Y}"/> 

Теперь, это работает отлично, но маркеры легенды отображать различные цвета в случайном, который я генерировать для LineSeries. Я хочу, чтобы один и тот же цвет отображался для элемента легенды для LineSeries и самого Lineseries. Итак, я стиль пункта легенды, как показано ниже

<Style x:Key="TestSuiteLegendItemStyle" 
     TargetType="{x:Type ChartingToolkit:LegendItem}"> 
    <Setter Property="IsTabStop" Value="False"/> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type ChartingToolkit:LegendItem}"> 
      <Border Background="{TemplateBinding Background}" 
        BorderBrush="{TemplateBinding BorderBrush}" 
        BorderThickness="{TemplateBinding BorderThickness}"> 
       <StackPanel Orientation="Horizontal"> 
        <Rectangle Width="8" 
           Height="8" 
           Fill="{Binding Background}" 
           Stroke="{Binding BorderBrush}" 
           StrokeThickness="1" Margin="0,0,3,0" /> 
        <VisualizationToolkit:Title Content="{TemplateBinding Content}" /> 
       </StackPanel> 
      </Border> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

Мой вопрос как я могу «привязать» Rectangle.Fill в TestSuiteLegendItemStyle стиле, так что это тот же цвет, что и LineSeries в установленном LineDataPointStyle определен выше ? *

Спасибо за ваше время.


Редактировать. Я попытался установить в DependencyProperty, который держит Background цвет моего участка, как предложено ниже с помощью

<Style x:Key="LineDataPointStyle" 
     TargetType="ChartingToolkit:LineDataPoint"> 
    ... 
    <Setter Property="Background" 
      Value="{Binding RelativeSource={RelativeSource Self}, 
          Converter={StaticResource ColorBrushConverter}}"/> 
    ... 
</Style> 

Где я ammended преобразователь (как отмечено), так что я могу хранить случайно сгенерированный цвет фона, а затем использовать это в моей легенде

public class ColorToBrushConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, 
     object parameter, System.Globalization.CultureInfo culture) 
    { 
     Brush b = new SolidColorBrush(Utils.GenerateRandomColor()); 
     MultiChartExtensions.BackgroundBrushProperty = b; <= how to set the dependency property? 
     return b; 
    } 

    public object ConvertBack(object value, Type targetType, 
     object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 

где Dependency Property (DP) определяется как

public static readonly DependencyProperty BackgroundBrushProperty; 
public static void SetBackgroundBrush(DependencyObject DepObject, Brush value) 
{ 
    DepObject.SetValue(BackgroundBrushProperty, value); 
} 

public static Brush GetBackgroundBrush(DependencyObject DepObject) 
{ 
    return (Brush)DepObject.GetValue(BackgroundBrushProperty); 
} 

Я бы тогда искать, чтобы установить легенду фона с помощью этого DP через

<Style x:Key="TestSuiteLegendItemStyle" 
     TargetType="{x:Type ChartingToolkit:LegendItem}"> 
    <Setter Property="IsTabStop" Value="False"/> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type ChartingToolkit:LegendItem}"> 
      <Border Background="{TemplateBinding Background}" 
        BorderBrush="{TemplateBinding BorderBrush}" 
        BorderThickness="{TemplateBinding BorderThickness}"> 
       <StackPanel Orientation="Horizontal"> 
        <Rectangle Width="8" 
           Height="8" 
           Fill="{Binding MultiChart:MultiChartExtensions.BackgroundBrushProperty}" 
           Stroke="{Binding BorderBrush}" 
           StrokeThickness="1" Margin="0,0,3,0" /> 
        <VisualizationToolkit:Title Content="{TemplateBinding Content}" /> 
       </StackPanel> 
      </Border> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

Любая помощь с этим была бы оценена ...

ответ

5

Part I. Binding in ControlTemplate

Если вы хотите использовать Binding в ControlTemplate, вы должны использовать следующую конструкцию:

<ControlTemplate TargetType="{x:Type SomeControl}"> 
    <Rectangle Fill="{TemplateBinding Background}" /> 

Цитируется из MSDN:

TemplateBinding является оптимизированной формой Binding для сценариев шаблона, аналогично к связывающему построен с {Binding RelativeSource={RelativeSource TemplatedParent}}.

Notes about using TemplateBinding

TemplateBinding не работает вне шаблона или вне его свойства VisualTree, поэтому вы не можете даже использовать TemplateBinding внутри триггера шаблона. Кроме того, TemplateBinding не работает при применении к Freezable (по большей части искусственным причинам), например, VisualBrush. В таких случаях можно использовать Binding так:

<FreezableControl Property="{Binding RelativeSource={RelativeSource TemplatedParent}, 
            Path=Background}" /> 

Кроме того, вы всегда можете использовать альтернативу TemplateBinding:

<Rectangle Fill="{Binding RelativeSource={RelativeSource TemplatedParent}, 
          Path=Background}" /> 

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

<Rectangle Fill="{Binding Background, 
          RelativeSource={RelativeSource AncestorType={x:Type SomeControl}}, 
          Path=Background}" /> 

Part II. Notes about your version

В вашем случае это может привести к конфликту имен в ControlTemplate, поскольку вы уже используете Binding background для Border. Поэтому удалите это Переплет для Border или используйте другое свойство, такое как Tag или прилагается свойство зависимости для привязки Цвет фона.

Example of using

Вместо ChartingToolkit управления, взял в качестве контроля основы Button, потому что это легче продемонстрировать идею этого стиля.

Solution 1: using Tag

<Window.Resources> 
    <Style x:Key="TestButtonStyle" TargetType="{x:Type Button}"> 
     <Setter Property="BorderThickness" Value="0" /> 
     <Setter Property="IsTabStop" Value="False" /> 

     <Setter Property="Template"> 
      <Setter.Value> 
       <ControlTemplate TargetType="{x:Type Button}"> 
        <!-- Here we are set Tag for Border Background --> 
        <Border Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Tag}" 
          BorderThickness="{TemplateBinding BorderThickness}"> 

         <Grid> 
          <Rectangle Width="24" 
             Height="24" 
             Fill="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Background}" 
             Stroke="{TemplateBinding BorderBrush}" /> 

          <ContentPresenter Content="{TemplateBinding Content}" 
               HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
               VerticalAlignment="{TemplateBinding VerticalContentAlignment}" /> 
         </Grid> 
        </Border> 
       </ControlTemplate> 
      </Setter.Value> 
     </Setter> 
    </Style> 
</Window.Resources> 

<Grid> 
    <Button Name="TestButton" 
      Style="{StaticResource TestButtonStyle}" 
      Content="Test" 
      HorizontalContentAlignment="Center" 
      VerticalContentAlignment="Center" 
      Tag="Green" 
      Background="Aquamarine" 
      Width="100" 
      Height="100" /> 
</Grid> 

Output

enter image description here

Здесь для Rectangle установите два цвета: по умолчанию для Rectangle в категории для Border. Я не нахожу это хорошее решение, и вот почему:

  • Если Border и прямоугольник необходимо установить различные значения, такие как: фон, BorderThickness, BorderBrush и т.д. один Tag недостаточно.

  • С одним свойством названия должно быть четко указано его назначение, одно имя «тег» нам ничего не говорит.

Из этих недостатков можно сделать вывод о том, что мы должны найти альтернативу, в качестве альтернативы я использую расширитель-класс с прикрепленными свойствами зависимости от .

Extender класса ButtonExt.cs

public static class ButtonExt 
{ 
    #region RectangleBackground Property 

    public static readonly DependencyProperty RectangleBackgroundProperty; 

    public static void SetRectangleBackground(DependencyObject DepObject, Brush value) 
    { 
     DepObject.SetValue(RectangleBackgroundProperty, value); 
    } 

    public static Brush GetRectangleBackground(DependencyObject DepObject) 
    { 
     return (Brush)DepObject.GetValue(RectangleBackgroundProperty); 
    } 

    #endregion 

    #region RectangleBorderBrush Property 

    public static readonly DependencyProperty RectangleBorderBrushProperty; 

    public static void SetRectangleBorderBrush(DependencyObject DepObject, Brush value) 
    { 
     DepObject.SetValue(RectangleBorderBrushProperty, value); 
    } 

    public static Brush GetRectangleBorderBrush(DependencyObject DepObject) 
    { 
     return (Brush)DepObject.GetValue(RectangleBorderBrushProperty); 
    } 

    #endregion  

    #region Button Constructor 

    static ButtonExt() 
    { 
     #region RectangleBackground 

     PropertyMetadata BrushPropertyMetadata = new PropertyMetadata(Brushes.Transparent); 

     RectangleBackgroundProperty = DependencyProperty.RegisterAttached("RectangleBackground", 
                  typeof(Brush), 
                  typeof(ButtonExt), 
                  BrushPropertyMetadata); 

     #endregion 

     #region RectangleBorderBrush 

     RectangleBorderBrushProperty = DependencyProperty.RegisterAttached("RectangleBorderBrush", 
                  typeof(Brush), 
                  typeof(ButtonExt), 
                  BrushPropertyMetadata); 

     #endregion 
    } 

    #endregion 
} 

MainWindow.xaml

<Window.Resources> 
    <Style x:Key="TestButtonExtensionStyle" TargetType="{x:Type Button}"> 
     <Setter Property="Width" Value="80" /> 
     <Setter Property="Height" Value="80" /> 
     <Setter Property="Background" Value="Green" /> 
     <Setter Property="BorderBrush" Value="Pink" /> 
     <Setter Property="BorderThickness" Value="4" /> 

     <Setter Property="Template"> 
      <Setter.Value> 
       <ControlTemplate TargetType="{x:Type Button}"> 
        <Border Background="{TemplateBinding Background}" 
          BorderBrush="{TemplateBinding BorderBrush}" 
          BorderThickness="{TemplateBinding BorderThickness}"> 

         <Grid> 
          <Rectangle Fill="{TemplateBinding PropertiesExtension:ButtonExt.RectangleBackground}" 
             Stroke="{TemplateBinding PropertiesExtension:ButtonExt.RectangleBorderBrush}" 
             Width="30" 
             Height="30" /> 

          <ContentPresenter Content="{TemplateBinding Content}" 
               HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
               VerticalAlignment="{TemplateBinding VerticalContentAlignment}" /> 
         </Grid> 
        </Border> 
       </ControlTemplate> 
      </Setter.Value> 
     </Setter> 
    </Style> 
</Window.Resources> 

<Grid> 
    <Button Style="{StaticResource TestButtonExtensionStyle}" 
      PropertiesExtension:ButtonExt.RectangleBackground="Aquamarine" 
      PropertiesExtension:ButtonExt.RectangleBorderBrush="Black" 
      Content="Test" /> 
</Grid> 

Output

enter image description here

Part III. Setting values for dependency properties

При создании и зарегистрировать вложенное свойство зависимостей , вы должны объявить набор и получить методы работы с ним:

public static void SetRectangleBackground(DependencyObject DepObject, Brush value) 
{ 
    DepObject.SetValue(RectangleBackgroundProperty, value); 
} 

public static Brush GetRectangleBackground(DependencyObject DepObject) 
{ 
    return (Brush)DepObject.GetValue(RectangleBackgroundProperty); 
} 

Тогда работа с ними будет выглядеть следующим образом:

Set

ButtonExt.SetRectangleBackground(MyButton, Brushes.Red); 

Get

Brush MyBrush = ButtonExt.GetRectangleBackground(MyButton); 

Но в нашем случае это не так просто. Когда я использовал связанные свойства зависимостей проблемы при обновлении значений не было. Но в нашем случае свойство находится в шаблоне, и в моем случае не было обновления Button. Я попытался установить Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, в Binding и в объявлении собственности, GetBindingExpression().UpdateTarget(), но это было бесполезно.

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

В любом случае лучше не обновлять свойство зависимостей напрямую и привязать к нему свойство Model и в ViewModel установить значение.

Пример:

<Button Style="{StaticResource TestButtonExtensionStyle}" 
     adp:ButtonExt.RectangleBackground="{Binding Path=Model.RectBackground, 
                Mode=TwoWay, 
                UpdateSourceTrigger=PropertyChanged}" 
     adp:ButtonExt.RectangleBorderBrush="{Binding Path=Model.RectBorderBrush, 
                Mode=TwoWay, 
                UpdateSourceTrigger=PropertyChanged}" /> 

RectBackground, где и RectBorderBrush реализуют интерфейс INotifyPropertyChanged.

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

Например, работа с DataTemplate, вы можете увидеть мои ответы:

Make (create) reusable dynamic Views

One ViewModel for UserControl and Window or separate ViewModels

+1

Еще раз спасибо за другой ответ, я многому учусь из ваших ответов. Я видел, как вы упоминали использование «Tag» раньше, но здесь мне не ясно, как вы собираетесь использовать его для включения «Background»? Это персональный проект, поэтому я попытаюсь выполнить ваши предложения после работы сегодня вечером ... Еще раз спасибо. – MoonKnight

+1

Примечание. Я задал более общий вопрос: http://stackoverflow.com/questions/21293363/change-the-color-displayed-in-wpf-charting-toolkit-legend, который имеет активную награду. Ответ на этот вопрос будет сочетанием того, что я сделал в моем новом вопросе выше, и вашего ответа ... Я с радостью награду вас щедростью позже, если ваши предложения будут работать. – MoonKnight

+1

@Killercam: Пожалуйста, см. Мое редактирование. Вместо этого элементы управления «ChartingToolkit» взяли за основу «Button», потому что легче продемонстрировать идею этих стилей, и я лично не работал с «ChartingToolkit». Если есть проблемы с вашей реализацией «ChartingToolkit» или возникнут дополнительные вопросы, спросите пожалуйста. –

1

Набор x:Name по контролю LineSeries:

<ChartingToolkit:LineSeries x:Name="lineSeries"/> 

Затем вы можете связать в TestSuiteLegendItemStyle посредством связывания с использованием ElementName:

<Rectangle Fill="{Binding Background, ElementName=lineSeries}"/> 
+0

Спасибо за Ваш ответ я должен знать этот метод! Я пробовал это, и теперь он устанавливает прозрачность заливки. Вы знаете, почему это происходит? – MoonKnight

+1

Убедитесь, что привязка не нарушена. Я подозреваю, что оба элемента управления не лежат в одном и том же Визуальном дереве. Прозрачным может быть цвет по умолчанию. Проверьте окно вывода для любой ошибки привязки. –

+0

Это не работает. По какой-то причине он не привязан к определенному элементу через 'ElementName' ... – MoonKnight

Смежные вопросы