-2

I have created a simple UI with Command binding for the Button and Combo Box. My intention is to populate the list of Button based upon the combo box selected value on Lost Focus event as like below code.

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            ViewModel ViewModel = new ViewModel();
            this.DataContext = ViewModel;
            //Test Data Feeded
            ViewModel.SelectedServer = ViewModel.ServerList.First();
            ViewModel.MyList = new ObservableCollection<Model>();
            for (int i = 0; i < Convert.ToInt16(ViewModel.SelectedServer); i++)
            {
                ViewModel.MyList.Add(new Model() { Name = (i + 1).ToString() });
            }
            InitializeComponent();
            txtServer.Focus();
        }
    }
    public class ViewModel: BaseDataMdel
    {
        ObservableCollection<Model> _MyList;
        List<string> serverList;
        string selectedServer;
        void Validated()
        {
            //Bsaed upon some validation we repopulate the collection
            if (MyList == null) MyList = new ObservableCollection<Model>(); else MyList.Clear();
            for (int i = 0; i < Convert.ToInt16(SelectedServer); i++)
            {
                MyList.Add(new Model() { Name = (i + 1).ToString() });
            }
        }
        private void ActionRequestedEvent(object param)
        {
            switch(param.ToString())
            {
                case "DataSetServerLostFocus":
                    {
                        Validated();
                    }
                    break;
                default:
                    MessageBox.Show(param.ToString()); break;
            }
        }
        private ICommand _ActionCommand1;
        public ICommand ActionCommand1
        {
            get
            {
                if (_ActionCommand1 == null)
                    this._ActionCommand1 = new RelayCommand(param =>
                    {
                        ActionRequestedEvent(param);
                    });
                return _ActionCommand1;
            }
            set
            {
                _ActionCommand = value;
            }
        }
        private ICommand _ActionCommand;
        public ICommand ActionCommand
        {
            get
            {
                if (_ActionCommand == null)
                    this._ActionCommand = new RelayCommand(param =>
                    {
                        ActionRequestedEvent(param);
                    });
                return _ActionCommand;
            }
            set
            {
                _ActionCommand = value;
            }
        }

        public ObservableCollection<Model> MyList
        {
            get
            {
                return _MyList;
            }

            set
            {
                _MyList = value;
                RaisePropertyChanged(nameof(MyList));
            }
        }

        public List<string> ServerList
        {
            get
            {
                return serverList;
            }

            set
            {
                serverList = value;
                RaisePropertyChanged(nameof(ServerList));
            }
        }

        public string SelectedServer
        {
            get
            {
                return selectedServer;
            }

            set
            {
                selectedServer = value;
                RaisePropertyChanged(nameof(SelectedServer));
            }
        }

        public ViewModel()
        {
            ServerList = new List<string>() { "1", "2", "3", "4", "5" };
        }
    }

Xaml code

<Window x:Class="WpfApplication26.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:local="clr-namespace:WpfApplication26"
        mc:Ignorable="d" Name="Group"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center">
        <ComboBox Width="150" Name="txtServer" ItemsSource="{Binding ServerList}" SelectedItem="{Binding SelectedServer}" Height="25">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="LostFocus">
                    <i:InvokeCommandAction Command="{Binding ActionCommand}" CommandParameter="DataSetServerLostFocus"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </ComboBox>
        <GroupBox Header="Test Case" Width="175" Height="200">
            <ItemsControl ItemsSource="{Binding MyList}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal" Margin="2">
                            <TextBox Width="120" Text="{Binding Name}"/>
                            <Button Content=".." Height="25" Width="25" Margin="3 0 0 0"  
                                    Command="{Binding DataContext.ActionCommand,ElementName=Group}"
                                    CommandParameter="{Binding Name}"/>
                        </StackPanel>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </GroupBox>
    </StackPanel>
</Window>
  1. Run the code.
  2. Click the Button on the ListView.
  3. See that the command is not invoked on the first click.

I Know that is happening due to re-iteration of the collection.

steps Tried to overcome this:

  1. Store the button in the temp variable before re-iteration and invoke the button click event programmatically after the update, this helps for me to resolve my issue partially. Because

    Window.GetWindow(button)

returning null for the backup button.

Is there any way to over come this problem.

EldHasp
  • 6,079
  • 2
  • 9
  • 24
Bala
  • 59
  • 5
  • 2
    `Command="{Binding DataContext.ActionCommand,ElementName=Group}"` - But where is the element named "Group"? – EldHasp Jul 06 '21 at 08:31
  • @EldHasp i have update my full code – Bala Jul 06 '21 at 08:37
  • 1
    And explain why you are handling the LostFocus event? In your other topic (https://stackoverflow.com/questions/68253459/lost-focus-event-issue-in-wpf), you also handle this event. In WPF solutions, it is not customary to use events - are not Forms. Events are very rarely used in WPF. I think you are implementing your task incorrectly. – EldHasp Jul 06 '21 at 08:39
  • When you use ItemTemplate you have to use RelativeResource instead of ElementName – Nawed Nabi Zada Jul 06 '21 at 08:39
  • @EldHasp, Actually we have maintained on server combo box, User can change that server at any time. If the configured data is not avail on the selected server means we have to clear that data from the view for this purpose only we used the lost focus event. Moreover user can add the data dynamically in to our custom combo box from the view itself. So validate the entered text on lost focus is a valid on here – Bala Jul 06 '21 at 08:48
  • What is wrong with binding the SelectedItem to the ViewModel property and handling the change of this property? This implementation is much more typical in WPF solutions. – EldHasp Jul 06 '21 at 08:52
  • For what purpose do you specify obviously different algorithms for processing in one command? What should happen when you select an item in the ComboBox, and what when you click on the button? You also need to take into account that the button you have is an element of the list template and its actions (within the meaning) should be relative to this element. – EldHasp Jul 06 '21 at 08:55

1 Answers1

0

From your code, I am suspecting the issue is on the Button can't access the context. So please replace ElementName with RelativeResource like below it should work.

<Button Content=".." Height="25" Width="25" Margin="3 0 0 0" Command="{Binding DataContext.ActionCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"/>
Edmon
  • 108
  • 2
  • 5
  • You are not right. In this case, your option is completely identical to the TC option. The problem is not with the binding. This is confirmed by TC itself, having written that the button does not fire only if the command is called twice due to loss of focus and simultaneous pressing of the button. If you just press a button, then it works sanely. The problem is in the wrong layout, testing and using events where it is not needed. In another TC topic, I have already caught this error. But he re-does the wrong implementation. – EldHasp Jul 07 '21 at 07:56