2013-10-28 9 views
5

Mam 2 zestawy bloków tekstowych, niektóre z nich są w kontrolerze pozycji, a niektóre z nich nie są, Chcę utworzyć styl (bazujący tylko na typie), który ustawia tło blok tekstu, jeśli jego przodkiem jest ItemControl.
Mogę to zrobić za pomocą następującego kodu, ale problem polega na tym, że w dzienniku (i oknie wyjściowym) zostanie wyświetlony komunikat o błędzie związanym z danymi z powodu blokad tekstowych, które nie mają pozycji Element sterujący jako element nadrzędny. Czy istnieje lepszy sposób na wykonanie tego zadania i uniknięcie tego komunikatu o błędzie?Ustawianie stylu na podstawie istnienia typu przodka

<Grid> 
    <Grid.Resources> 
     <local:HasAncestorConverter x:Key="HasAncestorConverter" /> 
     <Style TargetType="TextBlock"> 

      <Style.Triggers> 
       <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}, Converter={StaticResource HasAncestorConverter}}" Value="True"> 
        <Setter Property="Background" 
          Value="{Binding Tag, 
          RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" /> 

       </DataTrigger> 
      </Style.Triggers> 

     </Style> 
    </Grid.Resources> 
    <StackPanel> 
     <TextBlock Text="Out of ItemControl" /> 
     <ItemsControl Tag="Blue" > 
      <TextBlock Text="Inside of ItemControl" /> 
     </ItemsControl> 
    </StackPanel> 

</Grid>  

Converter:

class HasAncestorConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     return value != null; 
    } 

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

Komunikat o błędzie:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''. BindingExpression:Path=; DataItem=null; target element is 'TextBlock' (Name=''); target property is 'NoTarget' (type 'Object')

Odpowiedz

1

Według użytkownika @ makc odpowiedzi I rozwiązać problem w ten sposób:

<Grid> 
    <Grid.Resources> 
     <local:HasAncestorConverter x:Key="HasAncestorConverter" /> 
     <Style TargetType="TextBlock"> 
      <Style.Triggers> 
       <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, 
             Converter={StaticResource HasAncestorConverter}, 
             ConverterParameter={x:Type ItemsControl}}" 
          Value="True"> 
        <Setter Property="Background" 
          Value="{Binding Tag, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" /> 
       </DataTrigger> 
      </Style.Triggers> 
     </Style> 
    </Grid.Resources> 
    <StackPanel> 
     <TextBlock Text="Out of ItemsControl" /> 
     <ItemsControl Tag="Blue"> 
      <TextBlock Text="Inside of ItemsControl" /> 
     </ItemsControl> 
    </StackPanel> 
</Grid> 

Przelicznik:

class HasAncestorConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter 
     , System.Globalization.CultureInfo culture) 
    { 
     object parent = null; 
     if (value != null && parameter != null && 
      parameter is Type && value is DependencyObject) 
     { 
      var control = value as DependencyObject; 
      Type t = parameter as Type; 
      parent = ParentFinder.FindParent(control, t); 
     } 
     return parent != null; 
    } 

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

klasa Pomocnik znalezienia rodzica określonego typu:
Uwaga: pomocnik znaleźć wszelkiego rodzaju rodzica w drzewie logicznym lub wizualnej. na przykład w moim przypadku ItemsControl jest rodzicem w drzewie logicznym i może być dziadkiem.

class ParentFinder 
{ 
    public static object FindParent(DependencyObject child, Type parentType) 
    { 
     object parent = null; 
     var logicalParent = LogicalTreeHelper.GetParent(child); 
     var visualParent = VisualTreeHelper.GetParent(child); 

     if (!(logicalParent == null && visualParent == null)) 
     { 
      if (logicalParent != null && logicalParent.GetType() == parentType) 
       parent = logicalParent; 
      else if (visualParent != null && visualParent.GetType() == parentType) 
       parent = visualParent; 
      else 
      { 
       if (visualParent != null) 
        parent = FindParent(visualParent, parentType); 
       if (parent == null && logicalParent != null) 
        parent = FindParent(logicalParent, parentType); 
      } 
     } 
     return parent; 
    } 
} 
0

Można pracować z FallbackValue lub TargetNullValue

Sprawdź ten link się:

http://dontcodetired.com/blog/post/FallbackValue-TargetNullValue-StringFormat-in-Silverlight-4.aspx

+0

To może być kolejny sposób, aby ustawić wartość, ale w tym przypadku będzie nadal pojawia się błąd na wyjściu, ponieważ wartość zerowa nie jest głównym problemem kwestia jest taka, że ​​przodek nie istnieje, chcę uniknąć tam błąd. –

+0

Nie przetestowałem, czy błąd zawsze zostanie odrzucony, ale zgodnie z msdn nie powinno tak być. FallbackValue oznacza wartość, która będzie używana, gdy żadna nie zostanie znaleziona. Więc nie sądzę, by został zgłoszony błąd, gdy na nieodnalezionej wartości zostanie użyta wartość FallbackValue. –

1

Użyj pozycji DataTemplate dla pozycji w pozycji ItemsControl.

<ItemsControl .... 
    <ItemsControl.ItemTemplate> 
     <DataTemplate> 
      <TextBlock Text="{Binding }" 
         Background="{Binding Tag, 
              RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/> 
     </DataTemplate> 
    </ItemsControl.ItemTemplate> 
    <sys:String>Inside of ItemControl</String> 
</ItemsControl> 

Zachowaj swój styl, jeśli potrzebujesz go dla innych seterów, po prostu usuń spust.

+0

Dzięki, ale to nie jest to, czego chcę. Uprościłem problem, aby móc to wyjaśnić. W moim przypadku jest to kontrola, a styl jest stylem domyślnym i powinien być ustawiony w "Generic.xaml" i będzie przekazywany do użytkownika kontrolki w pakiecie. Jeśli więc użytkownik umieści tę kontrolę w ItemControl, sterowanie będzie miało inny styl niż umieszczenie go poza ItemControl.Tak więc nie mam dostępu do kodu, który używa tej kontroli, a użytkownik kontroli nie ponosi odpowiedzialności za stosowanie stylu domyślnego. –

2

myślę rozwiązanie @Xameli jest to, czego naprawdę szuka ...
ale jeśli po prostu musi to zrobić w stylu to można to osiągnąć stosując VisualTreeHelper tak:

<Style.Triggers> 
      <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}, Converter={StaticResource HasAncestorConverter}}" Value="True"> 
       <Setter Property="Background" 
         Value="{Binding Tag,RelativeSource={RelativeSource Self}}" /> 

      </DataTrigger> 
     </Style.Triggers> 

konwertera :

class HasAncestorConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     //you probably will have to look a few levels up 
     var parent = VisualTreeHelper.GetParent(value) as ItemsControl; 
     return item != null; 
    } 

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

Dzięki, dałem mi pomysł, aby znaleźć rozwiązanie. Nawiasem mówiąc, ItemsControl należy znaleźć, zaznaczając drzewo logiczne. –

+0

@FredJand cieszę się, że mogę pomóc – makc

Powiązane problemy