2013-04-30 55 views
0

我剛剛遇到了WPF的另一個問題。普通的數據綁定似乎禁用觸發器

我有一個自定義控件的集合(複合的;由網格內的邊框等組成)。

我通過觸發器和綁定來控制顏色Background。我希望它們在mouseover(通過觸發器和自定義IValueConverter)實現時變得更暗,而且在選擇(即,單擊)時更改顏色。後者由常規Setter完成。

<Grid Width="150" Height="50" Margin="5"> 
    <Border CornerRadius="3" BorderBrush="Black" BorderThickness="0.5" > 
     <Border.Resources> 
      <local:BackgroundConverter x:Key="ColorConverter"/> 
     </Border.Resources> 
     <Border.Style> 
      <Style TargetType="Border">      
       <Style.Triggers> 
        <Trigger Property="Grid.IsMouseOver" Value="True"> 
         <Setter Property="Background" Value="{Binding Path=MyStatus, Converter={StaticResource ColorConverter}}"/> 
        </Trigger> 
        <Trigger Property="Grid.IsMouseOver" Value="False"> 
         <Setter Property="Background" Value="{Binding Path=MyStatus, Converter={StaticResource ColorConverter}}"/> 
        </Trigger> 
       </Style.Triggers> 
       <Setter Property="Background" Value="{Binding Path=MyStatus, Converter={StaticResource ColorConverter}}"/> 
      </Style> 
     </Border.Style> 
     <Grid>     
      <Grid.RowDefinitions> 
       <RowDefinition Height="0.6*"/> 
       <RowDefinition Height="0.5*"/> 
      </Grid.RowDefinitions>         
      <TextBlock Grid.Row="0" FontSize="14" TextAlignment="Center" VerticalAlignment="Center" FontWeight="Bold">      
       <Label Foreground="{Binding Path=TextColor}" Content="{Binding Path=ID}"/> 
      </TextBlock> 
      <TextBlock Grid.Row="1" FontSize="9" TextAlignment="Center" VerticalAlignment="Top" Margin="0" Padding="0"> 
       <Label Content="{Binding Path=StockName}"/> 
      </TextBlock> 
     </Grid> 
    </Border> 
</Grid> 

鼠標懸停效果工作正常,直到我點擊其中一個控件。觸發器在該點停止工作(除了未被點擊的控件外)。

我有點困惑。我如何在不禁用觸發器的情況下對某些用法進行綁定?

如有必要,我會提供更多細節。


@Rachel

您可以發佈您的代碼轉換器以後?我沒有看到如何將 IsMouseOver屬性傳遞給轉換器,因此它的 可能是一個靜態值,它在更改時未更新。並且由於 該值在觸發時不會改變,因此可能不會打擾 重新評估該值。你可能會更好使用 IMutliValueConverter並將其傳遞IsMouseOver和MyStatus,所以 隨時得到重新評估的任這兩個值改變

我沒有使用IMultiValueConverter。爲此,我創建了自己的「複合」對象,其中包括IsMouseOver。這是必要的,因爲背景顏色應該是在我自己的數據之外計算出來的(無論是選中還是映射項目)以及鼠標懸停(無論背景顏色,它應該在鼠標上略微變暗) 。

轉換代碼:

public class BackgroundConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     Debug.WriteLine("BackgroundConverter.Convert()"); 
     if (!(value is StockViewBackgroundStatus)) 
     { 
      throw new ArgumentException("value"); 
     } 
     var casted = (StockViewBackgroundStatus)value; 
     if (casted.IsNone) 
     { 
      if (casted.IsMouseOver) 
      { 
       return new SolidColorBrush(Colors.Gray); 
      } 
      else 
      { 
       return CreateLinearGradient(Colors.Gray, false); 
      } 
     } 
     switch (casted.Status) 
     { 
      case StockItem.Status.Mapped: 
       { 
        return CreateLinearGradient(Color.FromRgb(83, 165, 18), casted.IsMouseOver); 
       } 
      case StockItem.Status.MappedElsewhere: 
       { 
        return CreateLinearGradient(Color.FromRgb(104, 189, 36), casted.IsMouseOver); 
       } 
      case StockItem.Status.NotMapped: 
       { 
        return CreateLinearGradient(Colors.LightGray, casted.IsMouseOver); 
       } 
      default: 
       { 
        throw new NotImplementedException(casted.Status.ToString()); 
       } 
     }    
    } 

    private static LinearGradientBrush CreateLinearGradient(Color initial, bool darker) 
    { 
     var darkened = darker ? 0.1 : 0; 
     return new LinearGradientBrush(
      Lighten(initial, 1.05 - darkened), 
      Lighten(initial, 0.95 - darkened), 
      90); 
    } 

    private static Color Lighten(Color initial, double factor) 
    { 
     Func<double, double> trunc = (value) => (Math.Max(0, Math.Min(255, value))); 
     var resulting = Color.FromRgb(
      System.Convert.ToByte(trunc(initial.R * factor)), 
      System.Convert.ToByte(trunc(initial.G * factor)), 
      System.Convert.ToByte(trunc(initial.B * factor))); 
     return resulting; 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     Debug.WriteLine("BackgroundConverter.ConvertBack()"); 
     return value; 
    } 
} 

StockItem對象

public partial class StockItem : UserControl, INotifyPropertyChanged 
{ 
    private bool _empty; 
    public StockItem() 
    { 
     InitializeComponent(); 
     DataContext = this; 
    } 

    private string _id; 
    public string ID 
    { 
     get 
     { 
      return _id; 
     } 
     set 
     { 
      _id = value; 
      RaisePropertyChanged("ID"); 
     } 

    } 

    public Brush TextColor 
    { 
     get 
     {     
      Color color = IsSelected ? Colors.White : Colors.Black; 
      return new SolidColorBrush(color); 
     } 
    } 

    private string _stockName; 
    public string StockName 
    { 
     get 
     { 
      return _stockName; 
     } 
     set 
     { 
      _stockName = value; 
      RaisePropertyChanged("StockName"); 
     } 
    } 

    StockViewBackgroundStatus _status; 
    public StockViewBackgroundStatus MyStatus 
    { 
     get 
     { 
      return new StockViewBackgroundStatus() 
      { 
       IsMouseOver = this.IsMouseOver, 
       IsNone = IsEmpty, 
       Status = MappingStatus 
      }; 
     } 
     set 
     { 
      _status = value; 
      Debug.WriteLine("in " + ID + "..."); 
      Debug.WriteLine("RaisePropertyChanged(\"IsMouseOver\")"); 
      Debug.WriteLine("RaisePropertyChanged(\"MyStatus\")"); 

      RaisePropertyChanged("IsMouseOver"); // added, but doesn't help 
      RaisePropertyChanged("MyStatus"); 
     } 
    } 

    public bool IsEmpty 
    { 
     get 
     { 
      return _empty; 
     } 
    } 

    public static StockItem EmptyStock 
    { 
     get 
     { 
      return new StockItem() 
      { 
       _empty = true, 
       ID = "none", 
       Name = String.Empty 
      }; 
     } 
    } 

    internal EventHandler Selected 
    { 
     get; 
     set; 
    } 

    private Status _mappingStatus; 
    public Status MappingStatus 
    { 
     get 
     { 
      return _mappingStatus; 
     } 
     set 
     { 
      _mappingStatus = value; 
      Debug.WriteLine("in " + ID + "..."); 
      Debug.WriteLine("RaisePropertyChanged(\"MappingStatus\")"); 
      Debug.WriteLine("RaisePropertyChanged(\"TextColor\")"); 
      RaisePropertyChanged("MappingStatus"); 
      RaisePropertyChanged("TextColor"); 

      MyStatus = new StockViewBackgroundStatus() { IsMouseOver = this.IsMouseOver, IsNone = _empty, Status = value };     

      if (value == Status.Mapped && Selected != null) 
      { 
       Selected(this, null); 
      } 
     } 
    } 

    public bool IsSelected 
    { 
     get 
     { 
      return MappingStatus == Status.Mapped; 
     } 
    } 

    public enum Status 
    {    
     Mapped, 
     MappedElsewhere, 
     NotMapped 
    } 

    protected void RaisePropertyChanged(string property) 
    { 
     if (PropertyChanged == null) 
     { 
      return; 
     } 
     PropertyChanged(this, new PropertyChangedEventArgs(property)); 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
} 

視圖類封裝集合(在一個我實際上設定爲DataContext

public class TargetStocks 
{ 
    public ObservableCollection<StockItem> AllStocks 
    { 
     get; 
     set; 
    } 

    public void Add(StockItem sv, EventHandler selected) 
    { 
     if (sv == null) 
     { 
      throw new ArgumentNullException("sv"); 
     } 
     sv.MouseDown += sv_MouseDown; 
     if (selected != null) 
     { 
      sv.Selected += selected; 
     } 
     if (AllStocks == null) 
     { 
      AllStocks = new ObservableCollection<StockItem>(); 
     } 
     AllStocks.Add(sv); 
    } 

    public void AddRange(IEnumerable<StockItem> stocks, EventHandler selected) 
    { 
     foreach (var stock in stocks) 
     { 
      Add(stock, selected); 
     } 
    } 

    void sv_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) 
    { 
     if (!(sender is StockItem)) 
     { 
      return; 
     } 
     var sv = (StockItem)sender; 
     foreach (StockItem stock in AllStocks) 
     { 
      if (stock.MappingStatus == StockItem.Status.Mapped) 
      { 
       // this seems to kill the trigger 
       stock.MappingStatus = StockItem.Status.NotMapped; 
      } 
      if (stock == sv && sv.MappingStatus != StockItem.Status.Mapped) 
      { 
       // as above 
       stock.MappingStatus = StockItem.Status.Mapped; 
      } 
     } 
    } 
} 

如調試所示,在點擊任何庫存項目之前(或者在任何庫存項目的MappingStatus發生更改之前),鼠標懸停效果將起作用,而不會觸發轉換器。

Convert根本沒有被調用。

它在MouseDown事件處理程序中設置MappingStatus,似乎禁用(或分離)觸發器。

+3

您是否可以編輯您的問題以包含更改背景顏色的Click代碼?另外,你的'Click'事件是否在綁定中設置了source屬性,或者是邊框的'Background'屬性?如果是第二個,[Depdencey Property Precedence](http://msdn.microsoft.com/zh-cn/library/ms743230.aspx)指示在對象上設置的值優先於樣式值或觸發值。 – Rachel 2013-04-30 14:35:14

+0

正如@雷切爾問 - 你的'點擊'做什麼? – XAMeLi 2013-04-30 14:37:07

+0

也許我錯過了一些東西,但不要觸發器都返回相同的值?它們都將'Status'設置爲'{Binding Path = MyStatus,Converter = {StaticResource ColorConverter}}',它與默認的'Setter'的值相同,所以無論鼠標是否結束,背景顏色都將保持爲靜態它或沒有... – Rachel 2013-04-30 14:43:39

回答

1

由於正常的觸發器觸發,綁定不會被重新評估。

您可以將屬性更改爲新的綁定對象,但綁定本身只會在第一次使用時作爲觸發器的結果進行評估。

因此,當你IsMouseOver屬性改變,Background屬性從一個Binding對象轉換到另一個對象Binding但是綁定本身是沒有得到重新評估。

如果您提高了綁定值的PropertyChange通知,綁定得到重新評估。

作爲測試,向您的轉換器添加Debug行或斷點,並通過觸發關於綁定值的PropertyChanged通知對其進行測試。當它收到更改通知並重新評估時,您會看到它被擊中。

如果您希望在多個屬性中的一個更改時評估綁定值,請使用IMultiValueConverter

+0

從假期返回;我相應地更新了我的問題。感謝您的幫助:( – 2013-05-06 09:52:55

+0

@KonradMorawski謝謝。根據您的代碼,您應該考慮切換到「IMultiValueConverter」,因爲如果IsMouseOver或MappingStatus發生變化,綁定不會被重新評估,或者MyStatus值實際上並沒有改變爲一個新值,你可以創建一個'IMultiValueConverter',它接受來自UI的'IsMouseOver'屬性,以及來自數據的'MappingStatus'和'IsSelected'屬性。 – Rachel 2013-05-06 12:08:11

+0

謝謝,我會但爲什麼它(當前)只能在點擊之前正常工作? – 2013-05-06 12:11:31