I am currently experimenting with WPF. I created a demo project using the Windows Template Studio for Visual Studio.
Now I want to add a textbox that should be saved automatically by the MVVM pattern. I added the following XAML to my settings page.
<TextBlock
Style="{StaticResource BodyTextStyle}"
Text="Default Page" />
<TextBox Text="{Binding DefaultPage}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding SetDefaultPageCommand}" CommandParameter="{Binding DefaultPage}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
In SettingsViewModel
I added the following lines:
private readonly IDefaultPageService _defaultPageService;
private string _defaultPage;
public ICommand SetDefaultPageCommand => _setDefaultPageCommand ?? (_setDefaultPageCommand = new RelayCommand<string>(OnSetDefaultPage));
public string DefaultPage
{
get { return _defaultPage; }
set { Set(ref _defaultPage , value); }
}
private void OnSetDefaultPage(string defaultPage)
{
_defaultPageService.SetDefaultPage(defaultPage);
}
The service to save is implemented as:
public class DefaultPageService : IDefaultPageService
{
public DefaultPageService()
{
}
public void InitializeDefaultPage()
{
var theme = GetDefaultPage();
SetDefaultPage(theme);
}
public void SetDefaultPage(string defaultPage)
{
App.Current.Properties["DefaultPage"] = defaultPage.ToString();
}
public string GetDefaultPage()
{
if (App.Current.Properties.Contains("DefaultPage"))
{
var defaultPage = App.Current.Properties["DefaultPage"].ToString();
return defaultPage;
}
return "https://google.com";
}
}
Saving my new string works but unfortunately, my command is being called before the actual bound property has changed its value. I tried a bunch of different TextBox events already, such as KeyUp
and KeyDown
. The only event I found that works are LayoutUpdated
but this one is being fired over and over again by the GUI, so I'm pretty sure there is a better way.
Does anybody know how I can fix this?
The default behavior of the TextBox binding is to update the binding after the TextBox loses focus, so that is why your bound property only changes AFTER a user types and then performs any action to lost focus on the TextBox. You obviously can't use the TextChanged event because it will fire on every keystroke.
The best way to preserve your MVVM and rely on the bounding property to change first, is to get rid of your EventTrigger and your ICommand altogether, and simply take advantage of when your DefaultPage setter gets called.
In your SettingsViewModel:
private readonly IDefaultPageService _defaultPageService;
private string _defaultPage;
public string DefaultPage
{
get { return _defaultPage; }
set { Set(ref _defaultPage , value); OnSetDefaultPage(_defaultPage); }
}
private void OnSetDefaultPage(string defaultPage)
{
_defaultPageService.SetDefaultPage(defaultPage);
}