【发布时间】:2013-04-30 14:20:47
【问题描述】:
我刚刚遇到了 WPF 的另一个问题。
我有一组自定义控件(复合控件;由网格内的边框等组成)。
我通过触发器和绑定来控制他们的Background 颜色。我希望它们在鼠标悬停时变得更暗(通过触发器和自定义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>
鼠标悬停效果正常工作,直到我单击其中一个控件。触发器在该点停止工作(除了尚未单击的控件)。
我有点困惑。如何在不禁用触发器的情况下使用绑定?
如有必要,我会提供更多详细信息。
@雷切尔
您可以稍后发布您的转换器代码吗?我不明白如何 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。
【问题讨论】:
-
您能否编辑您的问题以包含更改背景颜色的点击代码?另外,您的
Click事件是在绑定中设置源属性,还是设置边框的Background属性?如果是第二个,Depdencey Property Precedence 规定在对象本身上设置的值优先于样式化或触发的值。 -
正如@Rachel 所问 - 你的
Click在做什么? -
也许我遗漏了一些东西,但你的触发器不是都返回相同的值吗?它们都将
Status设置为{Binding Path=MyStatus, Converter={StaticResource ColorConverter}},这也与默认Setter的值相同,因此无论鼠标是否在其上,您的背景颜色都将保持静态... -
@Rachel @XAMeLi
Click正在重置所有子项的MyStatus(引发INotifyPropertyChanged.PropertyChanged事件)(所有子项,因为当单击的项被选中时,其余项因此未被选中) -
@Rachel - 转换器将
IsMouseOver属性考虑在内。多亏了它,它最初可以工作。问题是触发器被禁用(?),从那时起转换器不再使用(通过调试代码验证)。
标签: wpf xaml data-binding triggers