2009-08-25 9 views
0

ObservableCollectionListBox에 바인딩되어 있고 강조 표시 메커니즘이 DataTrigger 초로 설정되어 있는데, 간단한 형광펜 세트 (디버그, 경고 등)를 사용하면 간단하게 스타일을 열거 할 수 있습니다. 이러한 옵션을 노출하는 뷰 모델에 바인딩 된 여러 데이터 트리거ListView WPF에서 새로 고침 제안 새로 고침

이제는 IsHighlighted(xxx) 개의 메소드 (속성 아님)로 노출되는 여러 개의 사용자 정의 형광펜을 지원하도록 시스템을 업그레이드했습니다.

ListView에서 시각적 상태 (스타일의 데이터 트리거)가 변경되었음을 알리려면 어떻게해야합니까? DataTrigger에 불을 붙잡을 수있는 "상쾌한"행사가 있습니까?

업데이트 : 나는 단순히 true 값을 반환하는 노출 특성 Active에 매핑 된 DataTrigger을 가지고 있지만, 그럼에도 불구하고 어떤 업데이트가 없습니다 :

<DataTrigger Binding="{Binding Highlight.Active}" 
      Value="true"> 
    <Setter Property="Background" 
      Value="{Binding Type, Converter={StaticResource typeToBackgroundConverter}}" /> 
    <Setter Property="Foreground" 
      Value="{Binding Type, Converter={StaticResource typeToForegroundConverter}}" /> 
</DataTrigger> 

답변

0

내가해야 할 일에 대한 설명과 함께 내 자신의 질문에 대답 할 것입니다.

WPF가 더 잘 알고 캐쉬 할 것으로 생각하는 부분에 대해서는 계속해서 대답하는 것처럼 긴 대답입니다. DataTrigger에 무조건적인 변경이있는 경우,이 중 어떤 것도 필요하지 않습니다!

먼저 문제의 일부를 다시 정리해 보겠습니다. 다른 스타일로 다른 행을 강조 표시 할 수있는 ListView가 있습니다. 처음에는 이러한 스타일이 디버그 및 오류와 같은 기본 제공 유형이었습니다. 이 경우 ViewModel의 행 변경 스타일을 DataTriggers로 쉽게 변경하고 각 업데이트를 즉시 수행 할 수 있습니다.

사용자 정의 형광펜을 허용하도록 업그레이드하고 나면 더 이상 래치 ​​할 속성이 없습니다. 동적으로 만든 경우에도 스타일은 알 수 없습니다.

이 문제를 해결하기 위해 HighlightingService을 구현했습니다 (내 ServiceLocator을 사용하고 IHightlightingServce 지원 인스턴스를 요청하면 언제든지 발견 할 수 있음). 이 서비스는 중요한 프로퍼티와 메소드의 수를 구현합니다

public ObservableCollection<IHighlighter> Highlighters { get; private set; } 

    public IHighlighterStyle IsHighlighted(ILogEntry logEntry) 
    { 
     foreach (IHighlighter highlighter in Highlighters) 
     { 
      if (highlighter.IsMatch(logEntry)) 
      { 
       return highlighter.Style; 
      } 
     } 
     return null; 
    } 

형광펜 컬렉션 공개적으로 액세스 할 수 있으므로를, 그 수집의 사용자가/방법을 추가 제거 구현하는 나의 필요를 부정/추가 항목을 제거 할 수 있음을 허용하기로 결정 . 그러나 서비스의 생성자에서 내부 IHighlighter 레코드가 변경되었는지를 알아야하기 때문에 해당 등록자의 CollectionChanged 속성에 관찰자를 등록하고 다른 콜백을 등록하여 항목 추가/제거에 반응합니다. 그러면 서비스를 시작할 수 있습니다 특정 INotifyCollectionChanged 이벤트. 이 모든으로

 [...] 
     // Register self as an observer of the collection. 
     Highlighters.CollectionChanged += HighlightersCollectionChanged; 
    } 

    private void HighlightersCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
    { 
     if (e.Action == NotifyCollectionChangedAction.Add) 
     { 
      foreach (var newItem in e.NewItems) 
      { 
       System.Diagnostics.Debug.Assert(newItem != null); 
       System.Diagnostics.Debug.Assert(newItem is IHighlighter); 

       if (e.NewItems != null 
        && newItem is IHighlighter 
        && newItem is INotifyPropertyChanged) 
       { 
        // Register on OnPropertyChanged. 
        IHighlighter highlighter = newItem as IHighlighter; 

        Trace.WriteLine(string.Format(
             "FilterService detected {0} added to collection and binding to its PropertyChanged event", 
             highlighter.Name)); 

        (newItem as INotifyPropertyChanged).PropertyChanged += CustomHighlighterPropertyChanged; 
       } 
      } 
     } 
     else if (e.Action == NotifyCollectionChangedAction.Remove) 
     { 
      foreach (var oldItem in e.OldItems) 
      { 
       System.Diagnostics.Debug.Assert(oldItem != null); 
       System.Diagnostics.Debug.Assert(oldItem is IHighlighter); 

       if (e.NewItems != null 
        && oldItem is IHighlighter 
        && oldItem is INotifyPropertyChanged) 
       { 
        // Unregister on OnPropertyChanged. 
        IHighlighter highlighter = oldItem as IHighlighter; 
        Trace.WriteLine(string.Format(
             "HighlightingService detected {0} removed from collection and unbinding from its PropertyChanged event", 
             highlighter.Name)); 

        (oldItem as INotifyPropertyChanged).PropertyChanged -= CustomHighlighterPropertyChanged; 
       } 
      } 
     } 
    } 

    private void CustomHighlighterPropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     if (sender is IHighlighter) 
     { 
      IHighlighter filter = (sender as IHighlighter); 
      Trace.WriteLine(string.Format("FilterServer saw some activity on {0} (IsEnabled = {1})", 
              filter.Name, filter.Enabled)); 

     } 
     OnPropertyChanged(string.Empty); 
    } 

은, 지금은 사용자가 등록 된 하이 라이터를 변경할 때마다 알고,하지만 난 아무것도에 트리거를 연결할 수 없다는 사실을 고정하지 않은, 그래서 변화를 반영 할 수 표시 스타일.

나는이 분류의 XAML 유일한 방법을 찾을 수 없습니다, 그래서 난 내 ListView에 포함 된 사용자 정의 컨트롤 제작 :

  1. 그것은 할당 : 이것은 몇 가지를 않습니다

    public partial class LogMessagesControl : UserControl 
    { 
        private IHighlightingService highlight { get; set; } 
    
        public LogMessagesControl() 
        { 
         InitializeComponent(); 
         highlight = ServiceLocator.Instance.Get<IHighlightingService>(); 
    
         if (highlight != null && highlight is INotifyPropertyChanged) 
         { 
          (highlight as INotifyPropertyChanged).PropertyChanged += (s, e) => UpdateStyles(); 
         } 
         messages.ItemContainerStyleSelector = new HighlightingSelector(); 
    
        } 
    
        private void UpdateStyles() 
        { 
         messages.ItemContainerStyleSelector = null; 
         messages.ItemContainerStyleSelector = new HighlightingSelector(); 
        } 
    } 
    

    HighlightingSelectorItemContainerStyleSelector (ListView는 messages)으로 지정하십시오.

  2. 또한 ViewModel 인 HighlighterService의 PropertyChanged 이벤트에 자신을 등록합니다.
  3. 변경 사항을 감지하면 ItemContainerStyleSelector의 현재 인스턴스 인 HighlightingSelector을 대체합니다 (Bea Costa가 필요로하는 웹에 주석이 있으므로 먼저 null로 바뀝니다).
  4. 그래서

는, 지금 필요한 모든 계정에 소요 HighlightingSelector 인 현재 강조 선택 (I가 그들이,이 재건 될 것이다 변경해야합니다) 알고, 내가 너무 일에 대해 걱정할 필요가 없습니다 많은). HighlightingSelector은 등록 된 형광펜을 반복하며 (활성화 된 경우) 스타일을 등록합니다. 나는 이것들을 비싸기 때문에 재 구축 할 때 Dictionary에 캐시하고, 사용자가 수동으로 상호 작용을 한 시점에서만 빌드되기 때문에이 일을 앞당기는 데 드는 비용이 눈에 띄지 않습니다.

런타임에서 염두에 두는 레코드에 HighlightingSelector.SelectStyle이 전달되면 적절한 스타일 (사용자의 원래 강조 표시 환경 설정을 기반으로 함)이 반환됩니다.

public class HighlightingSelector : StyleSelector 
{ 
    private readonly Dictionary<IHighlighter, Style> styles = new Dictionary<IHighlighter, Style>(); 

    public HighlightingSelector() 
    { 
     IHighlightingService highlightingService = ServiceLocator.Instance.Get<IHighlightingService>(); 

     if (highlightingService == null) return; 

     foreach (IHighlighter highlighter in highlightingService.Highlighters) 
     { 
      if (highlighter is TypeHighlighter) 
      { 
       // No need to create a style if not enabled, should the status of a highlighter 
       // change, then this collection will be rebuilt. 
       if (highlighter.Enabled) 
       { 
        Style style = new Style(typeof (ListViewItem)); 

        DataTrigger trigger = new DataTrigger(); 
        trigger.Binding = new Binding("Type"); 

        trigger.Value = (highlighter as TypeHighlighter).TypeMatch; 

        if (highlighter.Style != null) 
        { 
         if (highlighter.Style.Background != null) 
         { 
          trigger.Setters.Add(new Setter(Control.BackgroundProperty, 
                  new SolidColorBrush((Color) highlighter.Style.Background))); 
         } 
         if (highlighter.Style.Foreground != null) 
         { 
          trigger.Setters.Add(new Setter(Control.ForegroundProperty, 
                  new SolidColorBrush((Color) highlighter.Style.Foreground))); 
         } 
        } 

        style.Triggers.Add(trigger); 
        styles[highlighter] = style; 
       } 
      } 
     } 
    } 

    public override Style SelectStyle(object item, DependencyObject container) 
    { 
     ILogEntry entry = item as ILogEntry; 
     if (entry != null) 
     { 
      foreach (KeyValuePair<IHighlighter, Style> pair in styles) 
      { 
       if (pair.Key.IsMatch(entry) && pair.Key.Enabled) 
       { 
        return pair.Value; 
       } 
      } 
     } 
     return base.SelectStyle(item, container); 
    } 
} 
1

이 때 DataTrigger 변화의 상태, 이렇게하면 부모 UI 요소가 자동으로 새로 고침됩니다.

확인할 사항 몇 가지 : 1. 트리거의 입력 데이터가 실제로 예상대로 변경됩니다. 2. 트리거의 입력 데이터가 종속성 속성에 바인딩됩니다. 그렇지 않으면 값 업데이트시기를 알 수 없습니다.

XAML의 적절한 부분을 보여 주면 큰 도움이됩니다.

+0

datatriggers는 INotifyValueChanged 발표 속성에 대해 올바르게 작동하지만 사용자가 정의한 속성에 대해서는 속성이 없습니다. 현재 구현에서는 IValueConverter를 사용하여 내장 또는 사용자 정의와 관계없이 적절한 앞 ​​/ 뒤 색상을 얻습니다. 그러나 이미 표시된 색상은 내용에 변화가 없기 때문에 색상을 새로 고칩니다 (내용 변경). 기존 버튼 상태를 제 위치에 유지하고 기본 상태 (예 : 경고) 중 하나를 전환하면 잘 고쳐집니다. –

1

그냥 어떻게 든 항목의 색상을 설정하려는 경우, 당신은 당신이 원하는 것을 그 변환기를 쓸 수있다 :이 경우

<Thing Background="{Binding Converter={StaticResource MyItemColorConverter}}" /> 

컨버터는 IsHighlighted(xxx) 메소드를 호출하고 적절한를 반환 할 수 색은 Thing입니다.

하나 이상의 속성을 설정하려는 경우 여러 변환기를 사용할 수 있지만 어느 시점에서 아이디어가 붕괴되기 시작합니다.

또는 DataBinding에서 변환기를 사용하여 해당 항목이 특정 범주에 속하는지 확인한 다음 설정자를 적용 할 수 있습니다. 그것은 당신이 필요한 것에 달려 있습니다!

편집

난 그냥 질문을 다시 읽고 내가 마크 끝나요 깨달았다. 오케이.

INotifyPropertyChanged.PropertyChangedPropertyChangedEventArgs으로 지정하고 string.Empty을 사용하면 WPF 바인딩 인프라에서 모든 바인딩을 새로 고칠 수 있다고 생각합니다. 너 그거 해봤 니?

+0

하지만 나는 구속 할 것이 없습니다. 여러 개의 강조 표시가있을 수 있으며 사용자 정의 속성 중 하나도 바인드 가능 속성으로 표시되지 않습니다. 나는 하나의 바인딩 가능한 "일반"속성을 만들고 그것에 PropertyChanged 이벤트를 발생시킬 수 있다고 생각합니다. - "ItemSource"항목을 매개 변수로 사용하는 변환기의 일부 양식과 결합 시켰습니다. –

+0

'IValueConverter'를 사용하여 바인딩을하고있는 경우 다른 곳에 주석으로 설명하면 무언가에 바인딩됩니다. 위의 예에서'Path' 속성이 없다는 것은 '항목 자체에 바인딩'을 의미합니다. 제가 말한 것처럼 그 아이템이'PropertyChanged'를 실행할 때 어떤 일이 일어나는지 보려고 했습니까? –

+0

내 기록에서 "유형"필드에 바인딩됩니다. 변경되지 않습니다. ValueConverter는 ServiceLocator를 사용하여 해당 유형에 대한 색상을 찾습니다. 그러나 강조 표시를 활성화/비활성화 할 수 없으므로 스타일이 데이터를 트리거하지 않습니다. –

관련 문제