2011-01-29 2 views
1

사용자가 스타일을 지정할 수 있도록 여러 속성을 제공하는 ClockFace UserControl이 있습니다. 시계에는 두 개의 타원 개체가 테두리로 있습니다. 바깥 쪽 테두리와 안쪽 테두리.WPF DependencyProperty ... 이제 작동합니다 ... 지금은 ... 어떻게 ...?

<Ellipse Name="OuterBorder" Panel.ZIndex="5" StrokeThickness="{Binding BorderOuterThickness}" Stroke="{Binding BorderOuteBrush}" /> 
<Ellipse Name="InnerBorder" Panel.ZIndex="6" StrokeThickness="{Binding BorderInnerThickness}" Margin="{Binding StrokeThickness, ElementName=OuterBorder}" Stroke="{Binding BorderInnerBrush}"> 

public static readonly DependencyProperty BorderInnerBrushProperty = DependencyProperty.Register("BorderInnerBrush", typeof(Brush), typeof(ClockFace), new 

PropertyMetadata(new LinearGradientBrush(Color.FromRgb(118, 57, 57), Color.FromRgb(226, 185, 185), new Point(0.5, 0), new Point(0.5, 1)))); 

public Brush BorderInnerBrush 
{ 
    get { return (Brush)GetValue(BorderInnerBrushProperty); } 
    set { SetValue(BorderInnerBrushProperty, value); } 
} 

public static readonly DependencyProperty BorderOuterBrushProperty = DependencyProperty.Register("BorderOuterBrush", typeof(Brush), typeof(ClockFace), new 

PropertyMetadata(new LinearGradientBrush(Color.FromRgb(226, 185, 185), Color.FromRgb(118, 57, 57), new Point(0.5, 0), new Point(0.5, 1)))); 

public Brush BorderOuterBrush 
{ 
    get { return (Brush)GetValue(BorderOuterBrushProperty); } 
    set { SetValue(BorderOuterBrushProperty, value); } 
} 

스타일을 설정할 수 있으며 스타일을 바꿀 때 올바르게 업데이트됩니다. 나는 영리하고 BorderBrush라는 바로 가기 속성을 추가하여 BorderOuterBrush 속성에 전달한 다음 BorderInnerBrush 속성으로 반전 된 그라디언트와 함께 자체 복사본을 전달합니다. 스타일을 전환하여 속성을 초기화 한 다음이 코드를 실행할 수있게하려면 SetBorderBrushes 메서드를 호출하는 PropertyChangedCallback 메서드를 구현해야했습니다. BorderBrush 속성을 스타일로 설정하면

public new static readonly DependencyProperty BorderBrushProperty = DependencyProperty.Register("BorderBrush", typeof(Brush), typeof(ClockFace), new 

PropertyMetadata(OnBorderBrushChanged)); 

private static void OnBorderBrushChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) 
{ 
    ((ClockFace)dependencyObject).SetBorderBrushes((Brush)e.NewValue); 
} 

public new Brush BorderBrush 
{ 
    get { return (Brush)GetValue(BorderBrushProperty); } 
    set { SetValue(BorderBrushProperty, value); } 
} 

private void SetBorderBrushes(Brush brush) 
{ 
    if (brush != null) 
    { 
     BorderOuterBrush = brush; 
     Brush innerBrush = BorderOuterBrush.Clone(); 
     if (brush.GetType() == typeof(LinearGradientBrush) || brush.GetType() == typeof(RadialGradientBrush)) 
     { 
      foreach (GradientStop gradientStop in ((GradientBrush)innerBrush).GradientStops) 
      { 
       gradientStop.Offset = 1 - gradientStop.Offset; 
      } 
     } 
     BorderInnerBrush = innerBrush; 
    } 
} 

은 모두 내가 런타임에 스타일을 전환 할 때까지 즉 ... 잘 작동합니다. 이제 이상한 일이 생겼어. 설명해 줄게.

4 가지 프리셋 스타일이 있고 개별적으로 모두 잘 작동합니다. 그 중 세 개는 두 개의 BorderInnerBrush 및 BorderOuterBrush 속성을 사용하고 하나는 바로 가기 BorderBrush 속성을 사용합니다. ContextMenu를 사용하는 스타일과 리소스의 xaml 스타일에 액세스하는 일부 코드를 사용하여 스타일 사이를 전환하여 ClockFace 객체의 Style 속성으로 설정합니다.

바로 가기 속성을 문제없이 끝없이 사용하지 않는 세 가지 스타일을 전환 할 수 있습니다. 또한 바로 가기 속성을 사용하는 스타일로 전환 할 수 있으며 잘 보입니다. 이것은 기이함이 시작될 때입니다.

BorderBrush 속성을 사용하는 스타일로 전환하면 BorderInnerBrush 및 BorderOuterBrush 속성이 단순히 작동을 멈 춥니 다. 다양한 스타일로 설정된 미리 설정된 Brush 객체가 더 이상 두 개의 Ellipse 객체에 설정되지 않습니다. 일부 PropertyChangedCallback 메서드를 내부 및 외부 테두리 속성에 붙여 넣어 무슨 일이 있었는지 확인했습니다.

처음 응용 프로그램을 실행할 때 문제없이 바로 가기 속성을 사용하지 않는 세 가지 스타일로 전환 할 수 있습니다. 세 경계 Brush 속성의 PropertyChangedCallback 메서드에 중단 점을 넣고 프로그램을 디버깅했습니다. 이 세 가지 스타일로 각각 전환 할 때 내부 및 외부 테두리 속성 인 '콜백 메서드'중단 점은 예상대로 치게되었습니다. BorderBrush 속성을 사용하는 스타일로 전환 할 때 콜백 메서드의 중단 점이 발생했습니다. SetBorderBrushes 메서드는 두 개의 다른 테두리 Brush 개체를 설정하므로 안쪽 및 바깥 쪽 테두리 속성 인 '콜백 메서드'중단 점이 예상대로 맞았습니다.

다시 말하지만, 이상한 부분입니다. 이 후 다른 세 가지 스타일 중 하나로 전환하면 내부 및 외부 테두리 속성 인 '콜백 메서드'중단 점이 더 이상 적용되지 않습니다. 대신 BorderBrush 속성에 연결된 콜백 메서드의 중단 점이 e.NewValue 값이 null 인 경우입니다. SetBorderBrushes 메서드에서는 null 값이 무시되므로 더 이상 중단 점이 적용되지 않습니다.

추가 조사 후에 BorderBrushProperty DependencyProperty에 설정된 기본값이 없으므로 e.NewValue가 null임을 발견했습니다. 사실, 선언에 기본 Brush 개체를 추가 한 후에는 콜백 메서드에서 e.NewValue에 전달되는 Brush입니다. 두 개의 내부 및 외부 국경 프로퍼티의 콜백 메소드의 중단 점은이 후에 적중되지만, SetBorderBrushes 메소드에서 설정 되었기 때문에 그 것이다. 스타일의 BorderInnerBrush 및 BorderOuterBrush 개체에 설정된 Brush 개체는 BorderBrush 속성을 한 번 사용한 후에 이러한 속성에 전달되지 않습니다.

마지막주의 할 점은 스타일에서 값이 명시 적으로 설정되지 않은 경우 BorderBrushProperty 속성의 기본값이 설정되는 것처럼 내부 및 외부 테두리 DependencyProperty 개체의 기본값은 해당 값 BorderBrush 속성이 스타일에도 설정되어 있지 않은 경우에만 스타일에 명시 적으로 설정되지 않습니다.

저는 며칠 동안이 일에 매달 렸습니다. 쉬운 해결책이 바로 가기 속성을 제거하는 것이지만 차라리 무슨 일이 일어나고 있는지 확인하고 싶습니다. 나는이 문제를 해결할 수있는 밝은 불꽃에 충분한 정보를 제공해주기를 바랍니다. 아이디어 나 질문이 있으면 공유하십시오. 많은 감사합니다. 릭의 제안에 따라

UPDATE는 >>

, 나는 브러쉬 속성의 또 다른 쌍을 생성하고 다른 세 브러시 속성에 부착 된 콜백 메서드에서 그들을 설정합니다. 이 설정을 사용하면 시각적 인 문제없이 모든 스타일을 전환 할 수 있습니다. 또는 그렇게 생각했습니다.

이제 BorderBrush (바로 가기) 속성을 사용하는 스타일에서 두 개의 BorderInner 및 BorderOuter 속성을 설정하고 테두리가 올바르게 업데이트되는 스타일로 전환 할 수 있으므로 Rick에게 감사의 말을 전합니다. BorderBrush 속성을 설정하는 스타일에서 테두리 브러시를 명시 적으로 설정하지 않는 스타일로 전환 할 때 여전히 문제가 있습니다.

따라서 BorderInnerBrush 및 BorderOuterBrush 속성이 코드 배후에서 설정 한 후 바로 작동하지 않는 동일한 문제가 여전히 있습니다. 새로운 점은 두 개의 Brush 속성을 설정하는 스타일로 전환하면 다시 '깨울 것'인 것입니다 ... 테두리 브러시를 설정하지 않은 스타일로 전환하면 내부 속성과 외부 속성이 각각 설정됩니다. 기본값을 올바르게 다시 입력하십시오.

두 테두리 속성을 코드에서 설정하여 스타일이 설정되지 않았거나 기본값을 사용하지 못하게 된 스타일을 사용한 후에 만 ​​사용됩니다. 이것은 매우 이상합니다 ... 누구든지 그것을 해결할 수 있습니까?

업데이트 2 >>>

첫째, 시간 내 주셔서 감사 및 예 릭에 대한 너무 많은. Rick의 예제 코드를 복사하여 새 프로젝트에 붙여 넣은 후에는 Expression Blend 4가 ​​없기 때문에 몇 가지 변경 작업을 수행해야했습니다. 그런 다음

<ei:ChangePropertyAction TargetName="clock" PropertyName="Style" Value="{StaticResource styleBrush}"/> 

:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions" 

나는 또한 약간 각 ChangePropertyAction 선언을 변경했다 : 나는 SDK를 설치하고 지적 DLL을에 대한 참조를 추가, 대신에 다음과 같은 XML 네임 스페이스 선언을 사용했다 , 그것은 나를 위해 잘 작동하고 두 부분을 제외하고 내 실제 ClockFace 컨트롤의 해당 부분을 잘 나타내줍니다. 첫 번째는 내 스타일이 이벤트 핸들러를 통해 코드 뒤에서 전환된다는 것입니다. 이것은 분명 내 문제를 일으키지 않습니다. 두 번째 차이점은 내 버전의 ActualOuterBrush 및 ActualInnerBrush에서 기본 브러시 값을 설정했기 때문에 컨트롤 사용자가 테두리 브러시를 제공 할 필요가 없다는 것입니다.

내 마지막 문제는 BorderBrush 속성이 스타일에 설정된 후 테두리 속성이 명시 적으로 설정되지 않은 스타일에서 기본 내부 및 외부 테두리 브러시가 설정되지 않는다는 것입니다. 이 시점까지는 기본값이 설정된다는 것을 기억하십시오.그래서 저는 릭의 예를 실험하고 약간의 기본값을 추가했습니다 :

private static readonly DependencyPropertyKey ActualInnerBrushPropertyKey = DependencyProperty.RegisterReadOnly("ActualInnerBrush", typeof(Brush), typeof(Clock), new UIPropertyMetadata(Brushes.Teal)); 

private static readonly DependencyPropertyKey ActualOuterBrushPropertyKey = DependencyProperty.RegisterReadOnly("ActualOuterBrush", typeof(Brush), typeof(Clock), new UIPropertyMetadata(Brushes.Salmon)); 

이제이 프로젝트에는 비슷한 문제가 있습니다 ... 기본값은 재설정되지 않습니다. 나는이 행동에 당황 스럽다.

지금까지이 문제에 너무 오래 머물러 있었으며 Rick에서 여러 가지 유용한 것들을 배웠지 만 여전히 기본값과 관련된 문제가 있습니다. 가장 간단한 방법은 바로 가기 BorderBrush 속성을 제거하는 것입니다. Rick은 많은 도움을주었습니다. 저는 정답으로 답을주었습니다. 그러나이 문제를 읽는 사람이 기본값을 도울 수 있다면, 그 답을 듣고 감사하게 생각합니다.

답변

0

일단 속성이 설정되면 스타일이 더 이상 적용되지 않습니다. 변경된 핸들러에서 내부 및 외부 브러시 속성을 직접 설정하면 종속성 속성 하위 시스템은 변경이 실제로 스타일과 비교하여 발생했음을 알지 못합니다.

한 가지 해결책은 보호 된 읽기 전용 ActualInnerBorderBrushActualOuterBorderBrush 속성을 노출 한 다음 사용자 정의 가능한 세 가지 속성 모두가 각각의 변경 핸들러에서이 실제 값을 설정하도록하는 것입니다. 이 방식으로 사용자가 볼 수있는 속성은 서로 간섭하지 않고 항상 "사용자가 설정 한대로"설정할 수 있습니다.

편집 : 여기

다섯 개 속성의 전체 작업의 구현 : 여기

public class Clock : StackPanel 
{ 
    public Brush Brush 
    { 
     get { return (Brush)GetValue(BrushProperty); } 
     set { SetValue(BrushProperty, value); } 
    } 

    public static readonly DependencyProperty BrushProperty = 
     DependencyProperty.Register("Brush", typeof(Brush), typeof(Clock), 
     new UIPropertyMetadata((d, e) => (d as Clock).OnBrushChanged(d, e))); 

    public void OnBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     ActualInnerBrush = e.NewValue as Brush; 
     ActualOuterBrush = e.NewValue as Brush; 
    } 

    public Brush InnerBrush 
    { 
     get { return (Brush)GetValue(InnerBrushProperty); } 
     set { SetValue(InnerBrushProperty, value); } 
    } 

    public static readonly DependencyProperty InnerBrushProperty = 
     DependencyProperty.Register("InnerBrush", typeof(Brush), typeof(Clock), 
     new UIPropertyMetadata((d, e) => (d as Clock).OnInnerBrushChanged(d, e))); 

    public void OnInnerBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     ActualInnerBrush = e.NewValue as Brush; 
    } 

    public Brush OuterBrush 
    { 
     get { return (Brush)GetValue(OuterBrushProperty); } 
     set { SetValue(OuterBrushProperty, value); } 
    } 

    public static readonly DependencyProperty OuterBrushProperty = 
     DependencyProperty.Register("OuterBrush", typeof(Brush), typeof(Clock), 
     new UIPropertyMetadata((d, e) => (d as Clock).OnOuterBrushChanged(d, e))); 

    public void OnOuterBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     ActualOuterBrush = e.NewValue as Brush; 
    } 

    public Brush ActualInnerBrush 
    { 
     get { return (Brush)GetValue(ActualInnerBrushProperty); } 
     private set { SetValue(ActualInnerBrushPropertyKey, value); } 
    } 

    private static readonly DependencyPropertyKey ActualInnerBrushPropertyKey = 
     DependencyProperty.RegisterReadOnly("ActualInnerBrush", typeof(Brush), typeof(Clock), new UIPropertyMetadata()); 
    public static readonly DependencyProperty ActualInnerBrushProperty = ActualInnerBrushPropertyKey.DependencyProperty; 


    public Brush ActualOuterBrush 
    { 
     get { return (Brush)GetValue(ActualOuterBrushProperty); } 
     private set { SetValue(ActualOuterBrushPropertyKey, value); } 
    } 

    private static readonly DependencyPropertyKey ActualOuterBrushPropertyKey = 
     DependencyProperty.RegisterReadOnly("ActualOuterBrush", typeof(Brush), typeof(Clock), new UIPropertyMetadata()); 
    public static readonly DependencyProperty ActualOuterBrushProperty = ActualOuterBrushPropertyKey.DependencyProperty; 
} 

및 작동하는지 증명하기 위해 약간의 테스트 프로그램은 다음과 같습니다

<Grid> 
    <Grid.Resources> 
     <Style x:Key="styleBrush" TargetType="local:Clock"> 
      <Setter Property="Brush" Value="Red"/> 
     </Style> 
     <Style x:Key="styleInnerOnly" TargetType="local:Clock"> 
      <Setter Property="InnerBrush" Value="Green"/> 
     </Style> 
     <Style x:Key="styleInnerOuter" TargetType="local:Clock"> 
      <Setter Property="InnerBrush" Value="Blue"/> 
      <Setter Property="OuterBrush" Value="Yellow"/> 
     </Style> 
     <Style x:Key="styleEmpty" TargetType="local:Clock"/> 
    </Grid.Resources> 
    <StackPanel> 
     <local:Clock x:Name="clock" Orientation="Horizontal"> 
      <Rectangle Width="100" Height="100" Fill="{Binding ActualInnerBrush, ElementName=clock}"/> 
      <Rectangle Width="100" Height="100" Fill="{Binding ActualOuterBrush, ElementName=clock}"/> 
     </local:Clock> 
     <StackPanel Orientation="Horizontal"> 
      <Button Content="Default"> 
       <i:Interaction.Triggers> 
        <i:EventTrigger EventName="Click"> 
         <ei:ChangePropertyAction TargetObject="{Binding ElementName=clock}" PropertyName="Style" Value="{x:Null}"/> 
        </i:EventTrigger> 
       </i:Interaction.Triggers> 
      </Button> 
      <Button Content="BrushOnly"> 
       <i:Interaction.Triggers> 
        <i:EventTrigger EventName="Click"> 
         <ei:ChangePropertyAction TargetObject="{Binding ElementName=clock}" PropertyName="Style" Value="{StaticResource styleBrush}"/> 
        </i:EventTrigger> 
       </i:Interaction.Triggers> 
      </Button> 
      <Button Content="InnerOnly"> 
       <i:Interaction.Triggers> 
        <i:EventTrigger EventName="Click"> 
         <ei:ChangePropertyAction TargetObject="{Binding ElementName=clock}" PropertyName="Style" Value="{StaticResource styleInnerOnly}"/> 
        </i:EventTrigger> 
       </i:Interaction.Triggers> 
      </Button> 
      <Button Content="InnerOuter"> 
       <i:Interaction.Triggers> 
        <i:EventTrigger EventName="Click"> 
         <ei:ChangePropertyAction TargetObject="{Binding ElementName=clock}" PropertyName="Style" Value="{StaticResource styleInnerOuter}"/> 
        </i:EventTrigger> 
       </i:Interaction.Triggers> 
      </Button> 
      <Button Content="Empty"> 
       <i:Interaction.Triggers> 
        <i:EventTrigger EventName="Click"> 
         <ei:ChangePropertyAction TargetObject="{Binding ElementName=clock}" PropertyName="Style" Value="{StaticResource styleEmpty}"/> 
        </i:EventTrigger> 
       </i:Interaction.Triggers> 
      </Button> 
     </StackPanel> 
    </StackPanel> 
</Grid> 

이 예제는 비헤이비어를 사용합니다. 당신이 행동에 익숙하지 않은 경우, 익스프레션 블렌드 4 SDK를 설치하고 다음의 네임 스페이스를 추가

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" 

및 프로젝트에 System.Windows.InteractivityMicrosoft.Expression.Interactions를 추가합니다.

+0

나는 귀하의 솔루션을 시도하고 그것이 내 국경 문제를 해결하지 못했다고 유감스럽게 생각합니다. 이 설정을 사용하면 시각적 인 문제없이 모든 스타일을 전환 할 수 있습니다 ... 문제는 내 타원 개체가 Brush 속성에 바인딩되어 있어야한다는 것입니다. 공개로 설정하면 사용자가 설정하고 동일한 문제가 발생합니다. – Sheridan

+0

문제 없음 : 공개 읽기 전용으로 설정하십시오! 내가 보호받는 것을 제안한 유일한 이유는 소비자의 IntelliSense 목록에 나타나지 않기 위해서였습니다. 읽기 전용 의존성 프로퍼티를 생성하려면'DependencyProperty.RegisterReadOnly'를 사용하고 private setter를 사용하십시오. –

+0

콜백은 값이 변경 될 때만 호출되며 속성에 값이 있으면 스타일 설정자가 영향을 미치지 않으므로 값은 변경되지 않습니다. 따라서 값이 고정 된 이유와 속성 변경 핸들러가 호출되는 것을 멈추는 이유는 같은 원인에 기인합니다. 스타일은 명시 적으로 설정된 값을 재정의 할 수 없습니다. –

관련 문제