locked
Duell of the controls, which event fires first?

    Question

  • Hello together,

    I have a ListBox with some items, where one item is selected. And I have a TextBox with a text. When leaving the Textbox I have an event handler tbxEdit_LostFocus, when changing the selection I have an event handler liboItems_SelectionChanged. If I leave the TextBox and click on a new item to change the selection of the ListBox, the event handler liboItems_SelectionChanged is first called. I would have expected that the event handler tbxEdit_LostFocus is first called. But it is clear to me, that the event handler liboItems_SelectionChanged triggers the event-chain. Can anyone explain this ?

    Regards
    Torsten

    Wednesday, October 15, 2014 3:25 PM

Answers

  • Hi Torsten,

    I have modified the two aspects:

    1. I noticed that the TextBox will not lose focus when I click on ListBox after editing the text property, so I would recommend use TextChanged event of TextBox to work around.
    2. Created a new MyTextBox control to hold the current index of ListBox control. This would help to modify the ViewModel collection directly in code behind.

    You can see the code snippet in the following.

    In XAML.

    <Page
        x:Class="App49.MainPage"
        xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
        xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
        xmlns:local="using:App49"
        xmlns:d=http://schemas.microsoft.com/expression/blend/2008
        xmlns:mc=http://schemas.openxmlformats.org/markup-compatibility/2006
        mc:Ignorable="d">
        <Page.Resources>
            <local:PageViewModel x:Key="array1"></local:PageViewModel>
        </Page.Resources>
        <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
            <StackPanel>
                <ListBox x:Name="liboEnergy" ItemsSource="{Binding Source={StaticResource array1}, Path=Collections}" VerticalAlignment="Top" Height="500" Margin="60,10,0,0" HorizontalAlignment="Left" Width="400">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <Grid HorizontalAlignment="Left" VerticalAlignment="Top">
                                <TextBlock Grid.Column="0" Height="30" FontSize="20" Text="{Binding Path=Text}" VerticalAlignment="Top" HorizontalAlignment="Right"/>
                            </Grid>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
                <local:MyTextBox x:Name="tbxEdit" Margin="60,20,0,0" Width="400" HorizontalAlignment="Left" Height="40" FontSize="22" Text="{Binding Path=SelectedItem.Text, ElementName=liboEnergy}" Index="{Binding ElementName=liboEnergy, Path=SelectedIndex}" TextChanged="tbxEdit_TextChanged"/>
            </StackPanel>
        </Grid>
    </Page>

    In XAML.cs.

       public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
                Random rnd = new Random();
                for (int i = 0; i < 20; i++)
                {
                    PageViewModel.Collections.Add(new DataLogic() { Text = "hello" + rnd.Next(0, 101), Index = i });
                }
                var data = liboEnergy.CacheMode;
            }
            private void tbxEdit_TextChanged(object sender, TextChangedEventArgs e)
            {
                MyTextBox textbox = sender as MyTextBox;
                if (textbox != null && textbox.Index != -1)
                {
                    PageViewModel.Collections[textbox.Index].Text = textbox.Text;
                }
            }
        }
        public class MyTextBox : TextBox
        {
            public int Index
            {
                get { return (int)GetValue(IndexProperty); }
                set { SetValue(IndexProperty, value); }
            }
            public static readonly DependencyProperty IndexProperty =
                DependencyProperty.Register(
                    "Index",
                    typeof(int),
                    typeof(MyTextBox),
                    new PropertyMetadata("", null));
        }
        public class PageViewModel
        {
            private static System.Collections.ObjectModel.ObservableCollection<DataLogic> list = new System.Collections.ObjectModel.ObservableCollection<DataLogic>();
            public static System.Collections.ObjectModel.ObservableCollection<DataLogic> Collections
            {
                get { return list; }
            }
        }
        public class DataLogic : INotifyPropertyChanged
        {
            private string _text;
            public string Text
            {
                get
                {
                    return this._text;
                }
                set
                {
                    if (value != this._text)
                    {
                        this._text = value;
                        NotifyPropertyChanged();
                    }
                }
            }
            public int Index { get; set; }
            public event PropertyChangedEventHandler PropertyChanged;
            private void NotifyPropertyChanged(String propertyName = "")
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    

    The code snippets worked fine on my side, please let me know the result on my machine. If you still have questions, please feel free to let me know.

    Regards,


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place. Click HERE to participate the survey.

    Monday, October 27, 2014 7:51 AM
    Moderator

All replies

  • Hi Torsten,

    One possible explanation might be this:

    In WPF XAML there are two types of routed events: bubbling events and tunnelling events. The tunnelling events are always invoked before bubbling events. If the SelectionChanged was a tunnelling event, then it would explain this behaviour as the LostFocus event is bubbling. The caveat is that Windows Store Apps (as far as I know) do not have tunneling events.

    To test it further I have created an event handler for the Tapped event (which is also a bubbling event) - liboItems_Tapped. Just like the tbxEdit_LostFocus handler it was invoked after liboItems_SelectionChanged.

    It looks like SelectionChanged is a kind of Windows RT "tunneling" event that fires before any bubbling events such as LostFocus or Tapped.

    That's all for now. Maybe someone else can shed more light on that.

    Leszek


    My Apps


    • Edited by ata6502 Wednesday, October 15, 2014 5:49 PM
    Wednesday, October 15, 2014 5:49 PM
  • Hi Leszek,

    thanks for your help at the moment. I think, I must wait some more time for an answer.

    Regards

    Torsten

    Thursday, October 16, 2014 10:07 AM
  • Hi Torsten,

    I cannot repro your problem. When I leaves the TextBox and click a new item in ListBox. I can only see the TextBox event handler be called. I use the following code to see what event handler execute.

    private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                msg.Text += DateTime.Now.ToString() + "-- ListBox Event handler";
            }
            private void txt_LostFocus(object sender, RoutedEventArgs e)
            {
                msg.Text += DateTime.Now.ToString() + "-- TextBox Event handler";
            }
    

    Did you use FocusManager class to set the control focus? Can you tell me your scenario about this problem? Or can you send me a repro project. Use your OneDrive and share a link here.

    Regards,


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place. Click HERE to participate the survey.

    Thursday, October 23, 2014 9:24 AM
    Moderator
  • Hi Herro,

    thanks for your answer. If you click in the TextBox and change the text and than click on the page, the item in the ListBox is changed. If you click in the ListBox on another item, the edited item is not changed. The message shows that the ListBox-event is fired first.

    I have uploaded the sample as ZIP:

    http://www.file-upload.net/download-9730697/ListBoxBinding.zip.html

    Regards
    Torsten

    Thursday, October 23, 2014 10:42 AM
  • Hi Torsten,

    I’m sorry to tell you that I cannot open the file downloaded from the above link. It’s broken. Can you upload using your OneDrive?

    > If you click in the TextBox and change the text and then click on the page, the item in the ListBox is changed.

    In my test project, I can see only TextBox lost focus event.

    > If you click in the ListBox on another item, the edited item is not changed. The message shows that the ListBox-event is fired first.

    When I click in TextBox, modify the content and then click into a new ListBox item, I can see ListBox event first and then the TextBox lost focus event.

    Can you tell me the purpose of your scenario to help understanding?

    Regards,


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place. Click HERE to participate the survey.

    Friday, October 24, 2014 2:22 AM
    Moderator
  • Hi Herro,
    thanks for your answer. The aim is it to bind a SelectedItem of the ListBox to the TextBox. If you select an item, the TextBox shows the content and when you leave the TextBox after changing the content to select another item, the previous item is changed.

    Please try the link again, I have tested it successfully just now.

    The same problem is it, to go this way:

    <ListBox x:Name="liboEnergy" VerticalAlignment="Top" ItemsSource="{Binding EnergyCollect, Source={StaticResource dataLogic}}" Height="500" Margin="60,10,0,0" HorizontalAlignment="Left" Width="400">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid HorizontalAlignment="Left" VerticalAlignment="Top">
                    <TextBlock Grid.Column="0" Height="30" FontSize="20" Text="{Binding Text}" VerticalAlignment="Top" HorizontalAlignment="Right"/>
                </Grid>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    <TextBox x:Name="tbxEdit" Margin="60,20,0,0" Width="400" HorizontalAlignment="Left" Height="40" FontSize="22" Text="{Binding SelectedItem.Text, ElementName=liboEnergy, Mode=TwoWay}"/>

    Only if you leave the TextBox without selecting another item in the ListBox, it works. If you click in the ListBox to change the selection, the previous item is not changed.

    Regards
    Torsten

    Friday, October 24, 2014 1:07 PM
  • Hi Torsten,

    I have modified the two aspects:

    1. I noticed that the TextBox will not lose focus when I click on ListBox after editing the text property, so I would recommend use TextChanged event of TextBox to work around.
    2. Created a new MyTextBox control to hold the current index of ListBox control. This would help to modify the ViewModel collection directly in code behind.

    You can see the code snippet in the following.

    In XAML.

    <Page
        x:Class="App49.MainPage"
        xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
        xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
        xmlns:local="using:App49"
        xmlns:d=http://schemas.microsoft.com/expression/blend/2008
        xmlns:mc=http://schemas.openxmlformats.org/markup-compatibility/2006
        mc:Ignorable="d">
        <Page.Resources>
            <local:PageViewModel x:Key="array1"></local:PageViewModel>
        </Page.Resources>
        <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
            <StackPanel>
                <ListBox x:Name="liboEnergy" ItemsSource="{Binding Source={StaticResource array1}, Path=Collections}" VerticalAlignment="Top" Height="500" Margin="60,10,0,0" HorizontalAlignment="Left" Width="400">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <Grid HorizontalAlignment="Left" VerticalAlignment="Top">
                                <TextBlock Grid.Column="0" Height="30" FontSize="20" Text="{Binding Path=Text}" VerticalAlignment="Top" HorizontalAlignment="Right"/>
                            </Grid>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
                <local:MyTextBox x:Name="tbxEdit" Margin="60,20,0,0" Width="400" HorizontalAlignment="Left" Height="40" FontSize="22" Text="{Binding Path=SelectedItem.Text, ElementName=liboEnergy}" Index="{Binding ElementName=liboEnergy, Path=SelectedIndex}" TextChanged="tbxEdit_TextChanged"/>
            </StackPanel>
        </Grid>
    </Page>

    In XAML.cs.

       public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
                Random rnd = new Random();
                for (int i = 0; i < 20; i++)
                {
                    PageViewModel.Collections.Add(new DataLogic() { Text = "hello" + rnd.Next(0, 101), Index = i });
                }
                var data = liboEnergy.CacheMode;
            }
            private void tbxEdit_TextChanged(object sender, TextChangedEventArgs e)
            {
                MyTextBox textbox = sender as MyTextBox;
                if (textbox != null && textbox.Index != -1)
                {
                    PageViewModel.Collections[textbox.Index].Text = textbox.Text;
                }
            }
        }
        public class MyTextBox : TextBox
        {
            public int Index
            {
                get { return (int)GetValue(IndexProperty); }
                set { SetValue(IndexProperty, value); }
            }
            public static readonly DependencyProperty IndexProperty =
                DependencyProperty.Register(
                    "Index",
                    typeof(int),
                    typeof(MyTextBox),
                    new PropertyMetadata("", null));
        }
        public class PageViewModel
        {
            private static System.Collections.ObjectModel.ObservableCollection<DataLogic> list = new System.Collections.ObjectModel.ObservableCollection<DataLogic>();
            public static System.Collections.ObjectModel.ObservableCollection<DataLogic> Collections
            {
                get { return list; }
            }
        }
        public class DataLogic : INotifyPropertyChanged
        {
            private string _text;
            public string Text
            {
                get
                {
                    return this._text;
                }
                set
                {
                    if (value != this._text)
                    {
                        this._text = value;
                        NotifyPropertyChanged();
                    }
                }
            }
            public int Index { get; set; }
            public event PropertyChangedEventHandler PropertyChanged;
            private void NotifyPropertyChanged(String propertyName = "")
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    

    The code snippets worked fine on my side, please let me know the result on my machine. If you still have questions, please feel free to let me know.

    Regards,


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place. Click HERE to participate the survey.

    Monday, October 27, 2014 7:51 AM
    Moderator
  • Hi Herro,

    thanks for your answer and your great help. Maybe it's the implementation of the events, that the ListBox fires first. Maybe because of the trigger (of the event-chain) raised by the event of SelectionChanged. I don't know. I would have expected that the event handler tbxEdit_LostFocus is first called (who can enter the ListBox without loosing the focus of the TextBox before). I will use your work around, just tested.

    Regards
    Torsten

    Monday, October 27, 2014 6:36 PM