0

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>

コントロールの 1 つをクリックするまで、マウスオーバー効果は正しく機能します。トリガーはその時点で動作を停止します (クリックされていないコントロールは別として)。

私は少し困惑しています。トリガーを無効にせずにバインドを使用するにはどうすればよいですか?

必要に応じて詳細を提供します。


@レイチェル

後でコンバーターのコードを投稿できますか? IsMouseOver プロパティがどのようにコンバーターに渡されるのかわかりません。おそらく、変更されたときに更新されない静的な値です。また、トリガーされたときに値が変更されないため、値を再評価する必要はありません。IMutliValueConverter を使用して IsMouseOver と MyStatus を渡す方がよい場合があるため、これら 2 つの値のいずれかが変更されるたびに再評価されます。

を使用しませんでした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はまったく呼び出されません。

トリガーを無効にする (またはデタッチする) ように見えるイベント ハンドラーで を設定してMappingStatusいます。MouseDown

4

1 に答える 1

1

通常のトリガー起動の結果としてバインディングが再評価されることはありません。

プロパティを新しいバインディング オブジェクトに変更できますが、バインディング自体は、トリガーの結果として最初に使用されたときにのみ評価されます。

したがって、プロパティが変更されるIsMouseOverと、BackgroundプロパティはあるBindingオブジェクトから別のBindingオブジェクトに変更されますが、バインディング自体は再評価されません。

ただし、バインドされた値で PropertyChange 通知を発生させると、バインディング再評価されます。

テストとして、コンバーターに行またはブレークポイントを追加し、バインドされた値で通知Debugをトリガーしてテストします。PropertyChanged変更通知を受け取って再評価すると、ヒットすることがわかります。

複数のプロパティの 1 つが変更されたときにバインドされた値を評価する場合は、IMultiValueConverterを使用します。

于 2013-04-30T19:25:35.543 に答える