WPF: ICollectionView.Refresh() refreshing only when UIElement loses focus, instead of Text and Value Change - wpf

I have an ICollectionView, called RepozitorijumWrapper which should display my entities based on six fields. The fields are two TextBox and four DateTimePicker elements. Basically, every time any of those elements change (even by a number/letter) I want the list to update.
My DateTimePicker and TextBox elements are bound to DateTime and string properties, that have the RepozitorijumWrapper.Refresh() code in their setter. When I tested my application, the filter did work, but only once you left the field. After that I tried calling the Refresh() method from the controller, or rather using the TextChanged event for the TextBox elements, and the ValueChanged for the DateTimePickers. This changed nothing. The filter does work, but it isn't refreshed the way I'd like it to refresh.
Since my code is pretty much six copies of the same thing, with changed names and types, I'll only paste one instance of each relevant part of code.
Here's the property:
public string SifraTima
{
get {return sifraTima;}
set
{
if(!sifraTima.Equals(value))
{
sifraTima = value;
RepozitorijumWrapper.Refresh();
}
}
}
Here's the XAML for that property:
<TextBox x:Name="txtSifraTima" Grid.Column="1" Grid.Row="2" Margin="3,3,30,3" Text="{Binding Path=SifraTima}" TextChanged="txtSifraTima_TextChanged" />
Here's the event handler:
private void txtSifraTima_TextChanged(object sender, TextChangedEventArgs e)
{
presenter.RepozitorijumWrapper.Refresh();
}
Here's my ICollectionView, created in the constructor of my presenter class:
RepozitorijumWrapper = CollectionViewSource.GetDefaultView(rezervacije.Kolekcija);
RepozitorijumWrapper.Filter = itm =>
((Rezervacija)itm).SifraTerena.Contains(SifraTerena) &&
((Rezervacija)itm).SifraTima.Contains(SifraTima) &&
((Rezervacija)itm).VremeZauzimanja <= VremeZauzimanjaDo &&
((Rezervacija)itm).VremeZauzimanja >= VremeZauzimanjaOd &&
((Rezervacija)itm).VremeOslobadjanja <= VremeOslobadjanjaDo &&
((Rezervacija)itm).VremeOslobadjanja >= VremeOslobadjanjaOd;

Your binding on the Text property of the TextBox is set to only update the property after the focus is lost by default. To change that behavior, you can specify a value for UpdateSourceTrigger like so:
Text="{Binding Path=SifraTima, UpdateSourceTrigger=PropertyChanged}"
MSDN has more information on UpdateSourceTrigger

Related

OnPropertyChanged not firing when combobox first item is selected in a datagrid

I have a combobox in a WPF gridview like so:
<DataGridComboBoxColumn Header="Type" SelectedItemBinding="{Binding Path=Type, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Source={my:EnumValues {x:Type my:CommandTypes}}}"
MinWidth="100"/>
And a ViewModel behind it like this:
public class LoadSimCommand {
public CommandTypes Type
{
get
{
return mType;
}
set
{
mType = value;
switch (mType)
{
/* Set some dependency properties */
}
}
}
}
This works well except for one case: when I click on the combobox and select the first item from the list, the ViewModel is not updated. What's wrong?
As I was typing the question I thought of the answer. I still wanted to share the solution in case someone else is experiencing the same issue. The ViewModel is not updating because the ViewModel's CommandTypes property deafult value is 0 which translates to the first value in the enumeration. Therefore, when the first item is selected, the property isn't changed and therefore OnPropertyChanged is not fired because the property hasn't changed.
Now, I don't want to introduce an additional CommandType just to cover this so I decided to trick my datatype and make it Nullable like so:
public CommandTypes? Type;
Now the initial value of the Type is null and I can use value.HasValue to do some sanity testing. I also changed the initial states of the dependency properties I want to set in the switch to a "invalid" value so that my UI state is still consistent.
As a final step I added a RowEditEnding event handler with the following content:
private void DataGridView_RowEditEnding(object sender, DataGridRowEditEndingEventArgs e)
{
// Check if the entered type is not null
if (!(e.Row.Item is LoadSimCommand) ||
(e.Row.Item as LoadSimCommand).Type == null)
{
e.Cancel = true;
}
}
That way the row is not committed when the proper row type is not set.
Hope this is of some use to someone!

MVVM WPF databound checkbox won't firing events to the ViewModel for Checked and Unchecked states

I have a databound CheckBox inside a DataGrid, using WPF and MVVM;
<DataGridTemplateColumn Width="80" Header="Enabled">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=IsEnabled, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Name="theCheckbox" HorizontalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
This works fine and the CheckBox is being checked when IsEnabled is set. IsEnabled is a property in my collection of objects which I have bound to the DataGrid. What I want to do is to be able to validate if a specific rows CheckBox within the DataGrid should be allowed to be checked when the user selects it and if not remove their check and display a warning message something like "Row 1 cannot be checked without rows 5 and 9 being checked". I found out how to do this using code behind using the Checked and Unchecked properties of the CheckBox, but I am using MVVM and therefore want to handle things in the ViewModel associated with the View the DataGrid and CheckBox are in. How do I do this? I need a way of passing the Id field of the DataRow through too in order to identify which row I am working on, for arguments sake lets say the Id field is called BorderId.
Implement IDataErrorInfo on your objects, which is WPF's default interface for validation, and setup your validation code to validate if the checkbox can be checked or not
This is actually a bit trickier than normal because you are validating a property on your data item using data that doesn't exist on the data item, which means you need to provide some way of attaching unrelated validation to your object at run time.
The solution I usually use is to expose a ValidationDelegate from my object, which other objects can use to attach additional validation to the object. The code for it is posted on my blog, and it allows you to attach validation to your object like this:
public class MyViewModel
{
// Keeping this generic to reduce code here, but it
// should be a full property with PropertyChange notification
public ObservableCollection<SomeObject> SomeCollection { get; set; }
public MyViewModel()
{
SomeCollection = LoadDataGridObjects();
// Add the validation delegate to each object
foreach(var item in SomeCollection)
item.AddValidationDelegate(ValidateObject);
}
// Validation Delegate to verify the right checkboxes are checked
private string ValidateObject(object sender, string propertyName)
{
if (propertyName == "IsChecked")
{
var item = (SomeObject)sender;
if (item.Id == 1
&& !SomeCollection.First(p => p.Id == 5).IsChecked
&& !SomeCollection.First(p => p.Id == 9).IsChecked)
{
return "Row 1 cannot be checked without rows 5 and 9 being checked";
}
}
return null;
}
}
Could you add to the PropertyChanged event handler of each item in your list? Something like this:
foreach (Object x in List)
{
x.PropertyChanged += OnItemChanged;
}
Then in the change listener you can do whatever you like with the sender object including changing the IsEnabled property.
void OnItemChanged(object sender, PropertyChangedEventArgs e)
{
// change sender object properties or set bound label text here
}
If I understand your question, you have a checkbox and you want to control if it can be checked or not. Instead of letting the user check it at all times and push back an error message if you don't want to allow it, make your checkbox inactive in the first place if you don't want the user to check it.
To do this through your ViewModel (which is of course your DataContext), you need to bind the Checkbox's "Command" to a custom command in your ViewModel.
A WPF Command will provide the following functionality:
Automatically enable/disable a control based on custom logic
Handles the "action" event of your control (on a button of checkbox, it would be "click").

WPF UserControl with TextBox, use TextChanged event C#

I have a customer usercontrol that is a labeled TextBox (Border wrapped about a Label and a TextBox with the TextBox overlapping the label). I am finding few (working) examples on how to get the TextChanged function to work when called from my UserControl.
Just the textbox snippet:
<TextBox
FontSize="{Binding Path=DefaultFontSize}"
Style="{StaticResource WatermarkTextBox}"
Padding="{Binding Path=TextPadding}"
Tag="{Binding Path=TextValue}"
/>
I have tried using RoutedEventHandler like I did with my button's Click event, but it didn't work. How do I get it so when let's say I use on the window it is required:
<MyControl:LabeledTextBox
TextBoxChange="Some_Event"
TextValue="{Binding SomethingOrOther}"
/>
that it will fire off correctly and do the needed function
This question's really unclear. Do you want your user control to support a TextChanged event that gets raised when the text in the TextBox changes? If so, you need to implement it in the code-behind.
First, declare the event:
public event TextChangedEventHandler TextChanged;
Then, add an event handler to the TextBox:
<TextBox TextChanged="TextBox_TextChanged" ... />
and in the code-behind:
private void TextBox_TextChanged(object sender, TextChangedEventArgs args)
{
TextChangedEventHandler h = TextChanged;
if (h != null)
{
h(this, args);
}
}
If you are using MVVM (Or if your TextValue binding is binding to something you can get to and edit) you can put the logic you want executed in the setter.
So, lets say you are binding to a property MyTextBoxValue. Set the binding mode to two way in the XAML, and in the setter put the logic or call to another method.
If you want the code to fire every time you type, set UpdateSourceTrigger=PropertyChanged in XAML, if you want the code to fire only when text entry is "done" set UpdateSourceTrigger=LostFocus.

WPF ComboBox which updates its ItemsSource from the database as the Text property changes

I would like to have a ComboBox control on a form which will be used to search a list of investments as the user types. I can do this easily if I cache the entire collection of investments from the database on startup (currently 3,000 or so items), but I would prefer to not do that if it isn't necessary.
The behavior that I am trying to implement is:
The user types text into the editable ComboBox.
As the user enters each character, the database search function is triggered, narrowing down the search results with each successive keystroke.
As the search results are updated, the dropdown panel opens and displays the relevant matches
I have tried binding the Text property of the ComboBox to the InvestmentName (string) property on my ViewModel, and the ItemsSource property of the ComboBox to the InvestmentList (generic List) property on my ViewModel. When I do this, the Text property auto-completes from the ItemsSource, however the dropdown appears empty.
I have been able to achieve these results using a TextBox stacked on top of a ListBox, but it isn't very elegant and it takes up more screen real estate. I've also been able to get it to work with a TextBox stacked on top of a ComboBox, although the ComboBox steals the focus when the IsDropDownOpen property is set to "true" when there are valid search items. It also isn't very visually pleasing to use two controls for this.
I feel like I'm really close to getting it to work the way I want it to, but there is something which eludes me.
The XAML for this control is:
<ComboBox Height="23" Width="260" IsSynchronizedWithCurrentItem="True" HorizontalAlignment="Left"
ItemsSource="{Binding InvestmentList}" DisplayMemberPath="FullName"
IsDropDownOpen="{Binding DoShowInvestmentList}"
ItemsPanel="{DynamicResource ItemsTemplate}" IsEditable="True"
Text="{Binding Path=InvestmentName, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
The relevant ViewModel properties are:
private bool _doShowInvestmentList;
public bool DoShowInvestmentList
{
get { return _doShowInvestmentList; }
set { if (_doShowInvestmentList != value) { _doShowInvestmentList = value; RaisePropertyChanged("DoShowInvestmentList"); } }
}
private List<PFInvestment> _investmentList;
public List<PFInvestment> InvestmentList
{
get { return _investmentList; }
set { if (_investmentList != value) { _investmentList = value; RaisePropertyChanged("InvestmentList"); } }
}
private string _investmentName;
public string InvestmentName
{
get { return _investmentName; }
set
{
if (_investmentName != value)
{
_investmentName = value;
this.InvestmentList = DataAccess.SearchInvestmentsByName(value).ToList();
if (this.InvestmentList != null && this.InvestmentList.Count > 0)
this.DoShowInvestmentList = true;
else
this.DoShowInvestmentList = false;
RaisePropertyChanged("InvestmentName");
}
}
}
I've done a fair bit of research on this, but I haven't quite found the answer yet.
Check out this great article on CodeProject by... me :)
A Reusable WPF Autocomplete TextBox
Look towards the end for the Google suggest example, it is similar to what you need, where every keypress triggers another query to the server.

WPF Databind Before Saving

In my WPF application, I have a number of databound TextBoxes. The UpdateSourceTrigger for these bindings is LostFocus. The object is saved using the File menu. The problem I have is that it is possible to enter a new value into a TextBox, select Save from the File menu, and never persist the new value (the one visible in the TextBox) because accessing the menu does not remove focus from the TextBox. How can I fix this? Is there some way to force all the controls in a page to databind?
#palehorse: Good point. Unfortunately, I need to use LostFocus as my UpdateSourceTrigger in order to support the type of validation I want.
#dmo: I had thought of that. It seems, however, like a really inelegant solution for a relatively simple problem. Also, it requires that there be some control on the page which is is always visible to receive the focus. My application is tabbed, however, so no such control readily presents itself.
#Nidonocu: The fact that using the menu did not move focus from the TextBox confused me as well. That is, however, the behavior I am seeing. The following simple example demonstrates my problem:
<Window x:Class="WpfApplication2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<ObjectDataProvider x:Key="MyItemProvider" />
</Window.Resources>
<DockPanel LastChildFill="True">
<Menu DockPanel.Dock="Top">
<MenuItem Header="File">
<MenuItem Header="Save" Click="MenuItem_Click" />
</MenuItem>
</Menu>
<StackPanel DataContext="{Binding Source={StaticResource MyItemProvider}}">
<Label Content="Enter some text and then File > Save:" />
<TextBox Text="{Binding ValueA}" />
<TextBox Text="{Binding ValueB}" />
</StackPanel>
</DockPanel>
</Window>
using System;
using System.Text;
using System.Windows;
using System.Windows.Data;
namespace WpfApplication2
{
public partial class Window1 : Window
{
public MyItem Item
{
get { return (FindResource("MyItemProvider") as ObjectDataProvider).ObjectInstance as MyItem; }
set { (FindResource("MyItemProvider") as ObjectDataProvider).ObjectInstance = value; }
}
public Window1()
{
InitializeComponent();
Item = new MyItem();
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(string.Format("At the time of saving, the values in the TextBoxes are:\n'{0}'\nand\n'{1}'", Item.ValueA, Item.ValueB));
}
}
public class MyItem
{
public string ValueA { get; set; }
public string ValueB { get; set; }
}
}
I found that removing the menu items that are scope depended from the FocusScope of the menu causes the textbox to lose focus correctly. I wouldn't think this applies to ALL items in Menu, but certainly for a save or validate action.
<Menu FocusManager.IsFocusScope="False" >
Assuming that there is more than one control in the tab sequence, the following solution appears to be complete and general (just cut-and-paste)...
Control currentControl = System.Windows.Input.Keyboard.FocusedElement as Control;
if (currentControl != null)
{
// Force focus away from the current control to update its binding source.
currentControl.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
currentControl.Focus();
}
This is a UGLY hack but should also work
TextBox focusedTextBox = Keyboard.FocusedElement as TextBox;
if (focusedTextBox != null)
{
focusedTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
This code checks if a TextBox has focus... If 1 is found... update the binding source!
Suppose you have a TextBox in a window, and a ToolBar with a Save button in it. Assume the TextBox’s Text property is bound to a property on a business object, and the binding’s UpdateSourceTrigger property is set to the default value of LostFocus, meaning that the bound value is pushed back to the business object property when the TextBox loses input focus. Also, assume that the ToolBar’s Save button has its Command property set to ApplicationCommands.Save command.
In that situation, if you edit the TextBox and click the Save button with the mouse, there is a problem. When clicking on a Button in a ToolBar, the TextBox does not lose focus. Since the TextBox’s LostFocus event does not fire, the Text property binding does not update the source property of the business object.
Obviously you should not validate and save an object if the most recently edited value in the UI has not yet been pushed into the object. This is the exact problem Karl had worked around, by writing code in his window that manually looked for a TextBox with focus and updated the source of the data binding. His solution worked fine, but it got me thinking about a generic solution that would also be useful outside of this particular scenario. Enter CommandGroup…
Taken from Josh Smith’s CodeProject article about CommandGroup
Simple solution is update the Xaml code as shown below
<StackPanel DataContext="{Binding Source={StaticResource MyItemProvider}}">
<Label Content="Enter some text and then File > Save:" />
<TextBox Text="{Binding ValueA, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Text="{Binding ValueB, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
I've run into this issue and the best solution I've found was to change the focusable value of the button (or any other component such as MenuItem) to true:
<Button Focusable="True" Command="{Binding CustomSaveCommand}"/>
The reason it works, is because it forces the button to get focused before it invokes the command and therefore makes the TextBox or any other UIElement for that matter to loose their focus and raise lost focus event which invokes the binding to be changed.
In case you are using bounded command (as I was pointing to in my example), John Smith's great solution won't fit very well since you can't bind StaticExtension into bounded property (nor DP).
Have you tried setting the UpdateSourceTrigger to PropertyChanged? Alternatively, you could call the UpdateSOurce() method, but that seems like a bit overkill and defeats the purpose of TwoWay databinding.
Could you set the focus somewhere else just before saving?
You can do this by calling focus() on a UI element.
You could focus on whatever element invokes the "save". If your trigger is LostFocus then you have to move the focus somewhere. Save has the advantage that it isn't modified and would make sense to the user.
Since I noticed this issue is still a pain in the ass to solve on a very generic way, I tried various solutions.
Eventually one that worked out for me:
Whenever the need is there that UI changes must be validated and updated to its sources (Check for changes upon closeing a window, performing Save operations, ...), I call a validation function which does various things:
- make sure a focused element (like textbox, combobox, ...) loses its focus which will trigger default updatesource behavior
- validate any controls within the tree of the DependencyObject which is given to the validation function
- set focus back to the original focused element
The function itself returns true if everything is in order (validation is succesful) -> your original action (closeing with optional asking confirmation, saveing, ...) can continue. Otherwise the function will return false and your action cannot continue because there are validation errors on one or more elements (with the help of a generic ErrorTemplate on the elements).
The code (validation functionality is based on the article Detecting WPF Validation Errors):
public static class Validator
{
private static Dictionary<String, List<DependencyProperty>> gdicCachedDependencyProperties = new Dictionary<String, List<DependencyProperty>>();
public static Boolean IsValid(DependencyObject Parent)
{
// Move focus and reset it to update bindings which or otherwise not processed until losefocus
IInputElement lfocusedElement = Keyboard.FocusedElement;
if (lfocusedElement != null && lfocusedElement is UIElement)
{
// Move to previous AND to next InputElement (if your next InputElement is a menu, focus will not be lost -> therefor move in both directions)
(lfocusedElement as UIElement).MoveFocus(new TraversalRequest(FocusNavigationDirection.Previous));
(lfocusedElement as UIElement).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
Keyboard.ClearFocus();
}
if (Parent as UIElement == null || (Parent as UIElement).Visibility != Visibility.Visible)
return true;
// Validate all the bindings on the parent
Boolean lblnIsValid = true;
foreach (DependencyProperty aDependencyProperty in GetAllDependencyProperties(Parent))
{
if (BindingOperations.IsDataBound(Parent, aDependencyProperty))
{
// Get the binding expression base. This way all kinds of bindings (MultiBinding, PropertyBinding, ...) can be updated
BindingExpressionBase lbindingExpressionBase = BindingOperations.GetBindingExpressionBase(Parent, aDependencyProperty);
if (lbindingExpressionBase != null)
{
lbindingExpressionBase.ValidateWithoutUpdate();
if (lbindingExpressionBase.HasError)
lblnIsValid = false;
}
}
}
if (Parent is Visual || Parent is Visual3D)
{
// Fetch the visual children (in case of templated content, the LogicalTreeHelper will return no childs)
Int32 lintVisualChildCount = VisualTreeHelper.GetChildrenCount(Parent);
for (Int32 lintVisualChildIndex = 0; lintVisualChildIndex < lintVisualChildCount; lintVisualChildIndex++)
if (!IsValid(VisualTreeHelper.GetChild(Parent, lintVisualChildIndex)))
lblnIsValid = false;
}
if (lfocusedElement != null)
lfocusedElement.Focus();
return lblnIsValid;
}
public static List<DependencyProperty> GetAllDependencyProperties(DependencyObject DependencyObject)
{
Type ltype = DependencyObject.GetType();
if (gdicCachedDependencyProperties.ContainsKey(ltype.FullName))
return gdicCachedDependencyProperties[ltype.FullName];
List<DependencyProperty> llstDependencyProperties = new List<DependencyProperty>();
List<FieldInfo> llstFieldInfos = ltype.GetFields(BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Static).Where(Field => Field.FieldType == typeof(DependencyProperty)).ToList();
foreach (FieldInfo aFieldInfo in llstFieldInfos)
llstDependencyProperties.Add(aFieldInfo.GetValue(null) as DependencyProperty);
gdicCachedDependencyProperties.Add(ltype.FullName, llstDependencyProperties);
return llstDependencyProperties;
}
}
The easiest way is to set the focus somewhere.
You can set the focus back immediately, but setting the focus anywhere will trigger the LostFocus-Event on any type of control and make it update its stuff:
IInputElement x = System.Windows.Input.Keyboard.FocusedElement;
DummyField.Focus();
x.Focus();
Another way would be to get the focused element, get the binding element from the focused element, and trigger the update manually. An example for TextBox and ComboBox (you would need to add any control type you need to support):
TextBox t = Keyboard.FocusedElement as TextBox;
if ((t != null) && (t.GetBindingExpression(TextBox.TextProperty) != null))
t.GetBindingExpression(TextBox.TextProperty).UpdateSource();
ComboBox c = Keyboard.FocusedElement as ComboBox;
if ((c != null) && (c.GetBindingExpression(ComboBox.TextProperty) != null))
c.GetBindingExpression(ComboBox.TextProperty).UpdateSource();
What do you think about this? I believe I've figured out a way to make it a bit more generic using reflection. I really didn't like the idea of maintaining a list like some of the other examples.
var currentControl = System.Windows.Input.Keyboard.FocusedElement;
if (currentControl != null)
{
Type type = currentControl.GetType();
if (type.GetMethod("MoveFocus") != null && type.GetMethod("Focus") != null)
{
try
{
type.GetMethod("MoveFocus").Invoke(currentControl, new object[] { new TraversalRequest(FocusNavigationDirection.Next) });
type.GetMethod("Focus").Invoke(currentControl, null);
}
catch (Exception ex)
{
throw new Exception("Unable to handle unknown type: " + type.Name, ex);
}
}
}
See any problems with that?
Using BindingGroup will help to understand and mitigate this kind of problem.
Sometimes we consider to apply MVVM model against WPF data bindings.
For example, we consider about mail's subject property:
<TextBox x:Name="SubjectTextBox" Text="{Binding Subject}" />
TextBox SubjectTextBox is on side of View.
The bound property like ViewModel.Subject will belong to ViewModel.
The problem is that changes remain to View in this case.
When we close the WPF window, WPF TextBox won't loose focus on window close.
It means data binding won't perform writing back, and then changes are lost silently.
Introducing of BindingGroup helps to control whether we should apply changes: from View to ViewModel.
BindingGroup.CommitEdit(); will ensure apply changes of direction View → ViewModel
BindingGroup.CancelEdit(); will ensure to discard changes on View.
If you don't call neither, changes are lost silently!
In the following sample, we attach RibbonWindow_Closing event handler so that we can deal with this case of problem.
XAML:
<R:RibbonWindow Closing="RibbonWindow_Closing" ...>
<FrameworkElement.BindingGroup>
<BindingGroup />
</FrameworkElement.BindingGroup>
...
</R:RibbonWindow>
C#
private void RibbonWindow_Closing(object sender, CancelEventArgs e) {
e.Cancel = !NeedSave();
}
bool NeedSave() {
if (!BindingGroup.CommitEdit()) {
// There may be validation error.
return false; // changes this to true to allow closing.
}
// Insert your business code to check modifications.
// return true; if Saved/DontSave/NotChanged
// return false; if Cancel
}
It should work.

Resources