简体   繁体   中英

Bind mouseLeftButtonDown for ListBoxItem and background color on PropertyChanged (mvvm, wpf)

sorry if you think this is a duplication of another post, but I tried every possible solution out there and I can't make it work.

The question is a two parter, but it's about the same code, so I thought I can ask the question in the same thread.

I try to do order system in C#, wpf, vs2015 using mvvm without any (hard coded) couplings. There is two things I need to do. First is to fire an event which I need to capture in the ViewModel, and second, when the number of articles is over a certain level, the background of the listboxitem for that article should be green (otherwise white/grey)

For this I use a ListBox and some listBoxItems.

MainWindow.xaml (the part that matters)

        <Window x:Class="Sequence_Application_2.GUI.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        xmlns:acb="clr-namespace:AttachedCommandBehavior;assembly=AttachedCommandBehavior"
        mc:Ignorable="d"
        ....
    <Window.DataContext>

<Grid Grid.Column="0" Margin="10">
                <ListBox x:Name="LstViewFlows" SelectedItem="{Binding SelectedFlow.Name}" ItemsSource="{Binding Flows.Keys}" >
                    <ListBox.ItemContainerStyle>
                        <Style TargetType="{x:Type ListBoxItem}" >
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding SelectedFlow.CanDeliver, UpdateSourceTrigger=PropertyChanged}" Value="true" >
                                    <Setter Property="ListBoxItem.Background" Value="DarkGreen" />
                                    <Setter Property="FontWeight" Value="Bold"/>
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </ListBox.ItemContainerStyle>
                    <i:Interaction.Triggers>
                        <i:EventTrigger EventName="MouseLeftButtonDown">
                            <i:InvokeCommandAction Command="{Binding SetSelectedCommand}"/>
                        </i:EventTrigger>
                    </i:Interaction.Triggers>
                </ListBox>
            </Grid>

Question 1)

I need to bind a command to a "MouseLeftButtonDown" event when I click on a listboxItem in a listbox. I tried the ACB-solution but can't get it to work. At last I tried the code above and I can't get it to trigger when I click on an item, but below the items, in the empty part of the listbox (not on a listboxitem), it tiggers as it should. Guess I need to rearange my xaml-file or something, but I tried for two days now I nothing seems to work.

The command is named "setSelectedCommand" and is implemented like this

internal class SetSelectedFlowCommand : ICommand
{
    private FlowsViewModel _flowsViewModel;

    public SetSelectedFlowCommand(FlowsViewModel flowsViewModel)
    {
        _flowsViewModel = flowsViewModel; 
    }

    /// <summary>
    /// 
    /// </summary>
    public event EventHandler CanExecuteChanged
    {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        _flowsViewModel.SetSelectedFlow();
    }
}

As I said, if I click on the ListBoxItem - nothing happends, but if i click in the ListBox - the command is fired (but nothing is "selected")

Question 2

As you see in the xaml above, I try to set the background color for a ListBoxitem, based on the value .CanDeliver in an object that is stored in a dictionary in the ViewModel. The variable Flow is the dictionary and SelectedFlow is supposed to be the flow that is selected.

This is an order system and CanDeliver is a variable that tells the operator/user if there is enough products to be delivered to the customer. If there are enough the listboxitem should be green, otherwise remain white/grey. Do you understand my question? Can I do it like this? Refering to an object inside a dictionary? (it's triggered by an INotifyPropertyChanged in the objects)

Hope you can help me because I have no more hair to pull from my head now ;-)

You don't need to use an event handler if all you want is to get the selectedItem. I have build an working example for you to show how to use binding only (MVVM) to achieve your requirements in your question:

C# (ViewModel):

using System;
using System.Collections.Generic;
using System.Windows;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            MyViewModel mvm = new MyViewModel()
            {
                Flows = new ObservableCollection<Flow>() 
                { 
                    new Flow() { Name = "Flow1" }, 
                    new Flow() { Name = "Flow2" }, 
                    new Flow() { Name = "Flow3" , Amount=1}, 
                    new Flow() { Name = "Flow4" } 
                }
            };
            this.DataContext = mvm;
        }
    }

    public class MyViewModel : ObservableObject
    {
        private Flow _selectedflow;
        public ObservableCollection<Flow> Flows
        {
            get;
            set;
        }

        public Flow SelectedFlow
        {
            get { return _selectedflow; }
            set
            {
                if (value != _selectedflow)
                {
                    _selectedflow = value;
                    RaisePropertyChanged("SelectedFlow");
                }
            }
        }
    }

    public class Flow : ObservableObject
    {
        private string _name;
        private int _amount;
        public string Name
        {
            get { return _name; }
            set
            {
                if (value != _name)
                {
                    _name = value;
                    RaisePropertyChanged("Name");
                }
            }
        }

        public bool CanDeliver
        {
            get
            {
                return Amount > 0;
            }
        }

        public int Amount
        {
            get { return _amount; }
            set
            {
                if (value != _amount)
                {
                    _amount = value;
                    RaisePropertyChanged("Amount");
                    RaisePropertyChanged("CanDeliver");
                }
            }
        }
    }

    public class ObservableObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            var handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected void RaisePropertyChanged(String propertyName)
        {
            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }
    }
}

XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="300" Width="350">
    <Grid>
        <StackPanel Orientation="Vertical">
        <ListBox SelectedItem="{Binding SelectedFlow}" ItemsSource="{Binding Flows}" DisplayMemberPath="Name">
            <ListBox.ItemContainerStyle>
                <Style TargetType="{x:Type ListBoxItem}" >
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding CanDeliver, UpdateSourceTrigger=PropertyChanged}" Value="true" >
                            <Setter Property="Background" Value="DarkGreen" />
                            <Setter Property="FontWeight" Value="Bold"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>
        <TextBlock Text="{Binding SelectedFlow.Name}"/>
        </StackPanel>
    </Grid>
</Window>

Result: You can see the Flow item with CanDeliver = True ( Amount>0 ) has a Green background. And the TextBlock is showing the SelectedFlow .

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM