This is a migrated thread and some comments may be shown as answers.

RadMenu stops controls losing focus and hence binding not executed

15 Answers 142 Views
Menu
This is a migrated thread and some comments may be shown as answers.
Scott Waye
Top achievements
Rank 2
Veteran
Iron
Scott Waye asked on 26 Mar 2021, 10:48 PM

Hi,

If a button is placed in a RadMenu that causes navigation (in a Frame/Page setup), then the button is not getting focus and hence any Bindings that are present where the focus is are not executed before navigation takes place.  I see https://www.telerik.com/forums/radmenu-problem-404186d89499 and https://www.telerik.com/forums/i-think-i-found-a-nasty-bug which look like the same problem.  Is there any solution for this problem aside from not using RadMenu that will work for the focus being in any control?  I note that in Silverlight RadMenu does not have this problem. 

15 Answers, 1 is accepted

Sort by
0
Vladimir Stoyanov
Telerik team
answered on 31 Mar 2021, 12:23 PM

Hello Scott,

May I ask you to share some more information about the scenario so that I can better understand it? Can you elaborate on what you mean by: "the button is not getting focus and hence any Bindings that are present where the focus is are not executed before navigation takes place"? Can you share some sample code demonstrating how the RadMenu is defined and possibly a video showing the scenario? 

Thank you in advance for any help you can provide. 

Regards,
Vladimir Stoyanov
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

0
Scott Waye
Top achievements
Rank 2
Veteran
Iron
answered on 31 Mar 2021, 04:14 PM

Sure,  take the attached solution.  I'm using Telerik WPF version 2020.3.1020.310.

Run it in  the debugger (VS2019).

Type something in the textBox under the menu.

Click the "Inside RadMenu" button

Click the "Outside RadMenu" button.

Check the Output window in VS, you'll see something like

TextBoxText when button is outside menu is
TextBoxText when button is inside menu is some text

Note that the binding is not executed for the button that is inside the RadMenu.  Your Silverlight controls do not do this.  This is a problem if the button causes navigation as any changes made by the user will not get detected.  Changing the binding to have `UpdateSourceTrigger=PropertyChanged` is a workaround, but produces undesirable side effects as it pushes the changes for every change to the TextBox which can result in a lot of effects in the viewmodel/model, if for example you have things recalculating on changes, adding to grids, etc.

I'd like to know how I can get the RadMenu to evaulate the bindings before executing the button Command, as your Silverlight controls do.

oh, I can't upload a solution, that's odd.  Anyway in the view add the xaml:

 

<Window x:Class="MenuFocus.MainWindow"
        xmlns:local="clr-namespace:MenuFocus"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition />
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
        <Button Content="Outside RadMenu" Command="{Binding InsideMenuButtonCommand}"/>
        <telerik:RadMenu VerticalAlignment="Top" ClickToOpen="False" IconColumnWidth="0" >
            <Button Content="Inside RadMenu" Command="{Binding OutsideMenuButtonCommand}"/>
        </telerik:RadMenu>
        </StackPanel>
        <TextBox Text="{Binding TextBoxText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Row="1"></TextBox>
    </Grid>
</Window>

 

CodeBehind for the Window:

using System.Windows;
 
namespace MenuFocus
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new ViewModel();
        }
    }
}

 

Code for ViewModel:

 

using System.Diagnostics;
using Telerik.Windows.Controls;

namespace MenuFocus
{
    public class ViewModel : ViewModelBase
    {
        public ViewModel()
        {
            InsideMenuButtonCommand = new DelegateCommand(_ =>
            {
                Debug.WriteLine($"TextBoxText when button is inside menu is {TextBoxText}");
            });
            OutsideMenuButtonCommand = new DelegateCommand(_ =>
            {
                Debug.WriteLine($"TextBoxText when button is outside menu is {TextBoxText}");
            });
        }

        public DelegateCommand OutsideMenuButtonCommand { get; set; }

        public DelegateCommand InsideMenuButtonCommand { get; set; }

        public string TextBoxText { get; set; }
    }
}

0
Scott Waye
Top achievements
Rank 2
Veteran
Iron
answered on 31 Mar 2021, 04:15 PM
Sorry I added `, UpdateSourceTrigger=PropertyChanged` to the Binding for the TextBox, delete that before running it.  I don't seem to be able to edit my previous post.
0
Scott Waye
Top achievements
Rank 2
Veteran
Iron
answered on 31 Mar 2021, 04:18 PM

Sorry again I realize the binding are mixed up.  Xaml should be

<Window x:Class="MenuFocus.MainWindow"
        xmlns:local="clr-namespace:MenuFocus"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition />
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
        <Button Content="Outside RadMenu" Command="{Binding OutsideMenuButtonCommand}"/>
        <telerik:RadMenu VerticalAlignment="Top" ClickToOpen="False" IconColumnWidth="0" >
                <Button Content="Inside RadMenu" Command="{Binding InsideMenuButtonCommand}"/>
        </telerik:RadMenu>
        </StackPanel>
        <TextBox Text="{Binding TextBoxText, Mode=TwoWay}" Grid.Row="1"></TextBox>
    </Grid>
</Window>

With output:

 

TextBoxText when button is inside menu is
TextBoxText when button is outside menu is some text

0
Vladimir Stoyanov
Telerik team
answered on 02 Apr 2021, 09:52 AM

Hello Scott,

Thank you for the shared code snippets. Note, that sample projects cannot be attached to forum posts, however they can be added to support tickets. 

I was able to replicate the described behavior on my end and it is the expected one. You can observe the same result when using the native Menu element as well:

<Grid>
        <Grid.DataContext>
            <local:ViewModel />
        </Grid.DataContext>

        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition />
        </Grid.RowDefinitions>
        <!--<StackPanel Orientation="Horizontal">
            <Button Content="Outside RadMenu" Command="{Binding OutsideMenuButtonCommand}"/>
            <telerik:RadMenu VerticalAlignment="Top" ClickToOpen="False" IconColumnWidth="0" >
                <Button Content="Inside RadMenu" Command="{Binding InsideMenuButtonCommand}"/>
            </telerik:RadMenu>
        </StackPanel>-->

        <StackPanel Orientation="Horizontal">
            <Button Content="Outside RadMenu" Command="{Binding OutsideMenuButtonCommand}"/>
            <Menu VerticalAlignment="Top"  >
                <Button Content="Inside RadMenu" Command="{Binding InsideMenuButtonCommand}"/>
            </Menu>
        </StackPanel>
        <TextBox Text="{Binding TextBoxText, Mode=TwoWay}" Grid.Row="1"></TextBox>
    
    </Grid>

You can find some more information about this in the following thread: Why don't the controls lose their focus when menu is clicked?

That said, if setting the UpdateSourceTrigger to PropertyChanged is not suitable, you can use the suggested in the thread approach of updating the binding when the button is clicked. I am attaching a sample project demonstrating this suggestion based on the shared example. 

I hope you find this helpful.

Regards,
Vladimir Stoyanov
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

0
Scott Waye
Top achievements
Rank 2
Veteran
Iron
answered on 05 Apr 2021, 06:49 PM

Thanks this example works, but if you change the Button in the Menu to a RadHyperlinkButton it does not.  Is there a solution when using a RadHyperlinkButton?

 

            <telerik:RadMenu VerticalAlignment="Top" ClickToOpen="False" IconColumnWidth="0" ><br>                <telerik:RadHyperlinkButton Content="Inside RadMenu" Command="{Binding InsideMenuButtonCommand}" Click="Button_Click"/><br>            </telerik:RadMenu>
0
Vladimir Stoyanov
Telerik team
answered on 06 Apr 2021, 11:01 AM

Hello Scott Waye,

Thank you for the shared code snippet. 

It seems that in that scenario the Keyboard.FocusedElement is the Hyperlink within the RadHyperlinkButton. With this in mind, you can avoid the logic for relying on the Keyboard.FocusedElement and update the TextBox binding directly. 

I am attaching the sample project updated to demonstrate what I have in mind. Do check it out and let me know, if it helps.

Regards,
Vladimir Stoyanov
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

0
Scott Waye
Top achievements
Rank 2
Veteran
Iron
answered on 08 Apr 2021, 10:20 PM
yeah, that's ok in this sample, but in reality I've got around 50 text boxes, so this approach isn't going to work.
0
Vladimir Stoyanov
Telerik team
answered on 13 Apr 2021, 01:33 PM

Hello Scott Waye,

What comes to mind in that scenario is to use a BindingGroup. Here is some sample code based on the shared project:

<StackPanel x:Name="stackPanel" Grid.Row="1">
            <StackPanel.BindingGroup>
                <BindingGroup Name="tbBindingGroup" />
            </StackPanel.BindingGroup>
            <TextBox x:Name="textBox" Text="{Binding TextBoxText, Mode=TwoWay, BindingGroupName=tbBindingGroup}"  />
            <TextBox  Text="{Binding TextBoxText, Mode=TwoWay, BindingGroupName=tbBindingGroup}"  />
            <TextBox  Text="{Binding TextBoxText, Mode=TwoWay, BindingGroupName=tbBindingGroup}"  />
            <TextBox  Text="{Binding TextBoxText, Mode=TwoWay, BindingGroupName=tbBindingGroup}"  />
            <TextBox  Text="{Binding TextBoxText, Mode=TwoWay, BindingGroupName=tbBindingGroup}"  />
            <TextBox  Text="{Binding TextBoxText, Mode=TwoWay, BindingGroupName=tbBindingGroup}"  />
            <TextBox  Text="{Binding TextBoxText, Mode=TwoWay, BindingGroupName=tbBindingGroup}"  />
            <TextBox  Text="{Binding TextBoxText, Mode=TwoWay, BindingGroupName=tbBindingGroup}"  />
            <TextBox  Text="{Binding TextBoxText, Mode=TwoWay, BindingGroupName=tbBindingGroup}"  />
        </StackPanel>
private void Button_Click(object sender, RoutedEventArgs e)
        {
            BindingExpression be = this.textBox.GetBindingExpression(TextBox.TextProperty);
            be.UpdateSource();
       
            foreach (var expression in this.stackPanel.BindingGroup.BindingExpressions)
            {
                expression.UpdateTarget();
            }
        }

Hope this helps.

Regards,
Vladimir Stoyanov
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

0
Scott Waye
Top achievements
Rank 2
Veteran
Iron
answered on 13 Apr 2021, 03:18 PM

Thanks for the workaround.  I could implement this I suppose .  Problem is I'm porting an existing app from Telerik's Silverlight  and I've got a few hundred controls to do so not its not that appealing :-)

I wonder if there is another solution with maybe a hidden textbox where I can move the focus.

0
Vladimir Stoyanov
Telerik team
answered on 16 Apr 2021, 11:28 AM

Hello Scott Waye,

The approach suggested in my previous reply would only be necessary, if the property does not throw a property changed notification. If it does, any other controls bound to it will be updated. Here is how this would look in the context of the shared sample project:

<StackPanel Grid.Row="1" >
            <TextBox x:Name="textBox" Text="{Binding TextBoxText, Mode=TwoWay}" />
            <TextBox  Text="{Binding TextBoxText, Mode=TwoWay}" />
            <TextBox  Text="{Binding TextBoxText, Mode=TwoWay}" />
            <TextBox  Text="{Binding TextBoxText, Mode=TwoWay}" />
            <TextBox  Text="{Binding TextBoxText, Mode=TwoWay}" />
            <TextBox  Text="{Binding TextBoxText, Mode=TwoWay}" />
            <TextBox  Text="{Binding TextBoxText, Mode=TwoWay}" />
            <TextBox  Text="{Binding TextBoxText, Mode=TwoWay}" />
            <TextBox  Text="{Binding TextBoxText, Mode=TwoWay}" />
        </StackPanel>
public class ViewModel : ViewModelBase
    {
        private string textBoxText;

        public ViewModel()
        {
            InsideMenuButtonCommand = new DelegateCommand(_ =>
            {
                Debug.WriteLine($"TextBoxText when button is inside menu is {TextBoxText}");
            });
            OutsideMenuButtonCommand = new DelegateCommand(_ =>
            {
                Debug.WriteLine($"TextBoxText when button is outside menu is {TextBoxText}");
            });
        }

        public DelegateCommand OutsideMenuButtonCommand { get; set; }

        public DelegateCommand InsideMenuButtonCommand { get; set; }

        public string TextBoxText
        {
            get
            {
                return this.textBoxText;
            }
            set
            {
                if(this.textBoxText != value)
                {
                    this.textBoxText = value;
                    this.OnPropertyChanged("TextBoxText");
                }
            }
        }
    }
private void Button_Click(object sender, RoutedEventArgs e)
        {
            BindingExpression be = this.textBox.GetBindingExpression(TextBox.TextProperty);
            be.UpdateSource();
        }

I do hope that you will find this approach suitable for your scenario. 

Regards,
Vladimir Stoyanov
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

0
Scott Waye
Top achievements
Rank 2
Veteran
Iron
answered on 19 Apr 2021, 05:53 PM
Yeah, but the textboxes are bound to different properties to this doesn't work.  Only if the focus is in `textBox` will this work.
0
Scott Waye
Top achievements
Rank 2
Veteran
Iron
answered on 19 Apr 2021, 07:51 PM

HI Vladimir,

 

What I went with is using the `LostKeyboardFocus` so I change the xaml to

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition />
    </Grid.RowDefinitions>
    <StackPanel Orientation="Horizontal">
    <Button Content="Outside RadMenu" Command="{Binding OutsideMenuButtonCommand}"/>
              <Menu VerticalAlignment="Top" >
                <Button Content="Inside RadMenu" Command="{Binding InsideMenuButtonCommand}" Click="Button_Click"/>
    </Menu>
    </StackPanel>
    <Grid Grid.Row="1" LostKeyboardFocus="UIElement_OnLostKeyboardFocus">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <TextBox Text="{Binding TextBoxText, Mode=TwoWay}"  x:Name="textBox"></TextBox>
        <TextBox Text="{Binding TextBoxText2, Mode=TwoWay}" Grid.Row="1"></TextBox>
    </Grid>
</Grid>

 

and in the code

 

private void CommitBindings(DependencyObject element)
{
    var localValueEnumerator = element.GetLocalValueEnumerator();
    while (localValueEnumerator.MoveNext())
    {
        var entry = localValueEnumerator.Current;
        if (BindingOperations.IsDataBound(element, entry.Property))
        {
            var bindingExpression = (BindingExpressionBase)entry.Value;
            bindingExpression.UpdateSource();
        }
    }
}
 
void UIElement_OnLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    var dObject = e.OldFocus as DependencyObject;
    if (dObject != null)
    {
        CommitBindings(dObject);
    }
}
0
Scott Waye
Top achievements
Rank 2
Veteran
Iron
answered on 19 Apr 2021, 07:52 PM
Which I took from https://stackoverflow.com/questions/57493/wpf-databind-before-saving
0
Vladimir Stoyanov
Telerik team
answered on 22 Apr 2021, 12:57 PM

Hello Scott Waye,

I am glad to hear that you found an approach that is suitable for your scenario. 

Thank you for sharing it with the community.

Regards,
Vladimir Stoyanov
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

Tags
Menu
Asked by
Scott Waye
Top achievements
Rank 2
Veteran
Iron
Answers by
Vladimir Stoyanov
Telerik team
Scott Waye
Top achievements
Rank 2
Veteran
Iron
Share this question
or