Databinding mode Explicit - wpf

I've got a listbox that contains a list of objects (lets say addresses)
The list box items source is bound to this observable collection
<ListBox x:Name="listDetails"
ItemsSource="{Binding}"
...
Then i've got a text box, this is bound to the name fild of the current object
<TextBox x:Name="textBoxName" Text="{Binding Name, UpdateSourceTrigger=Explicit}" />
So I expect that the Name property of my current object won't be change unless i explicitly update it..
However it is getting updated... any ideas to why?
Also this is in a window, if i close the window and reopen the window somehow the same selection on the listbox is preserved..
I'd expected once the window was closed then it would forget all about the current selection of it's listbox?

Figured out how to solve it, i added an event to the listgbox
private void OnListValueChanged(object sender, SelectionChangedEventArgs e)
{
ListBox lb = e.Source as ListBox;
if (lb != null)
{
object dc = null;
if (lb.SelectedIndex != -1)
dc = lb.Items[lb.SelectedIndex];
gridDetails.DataContext = dc;
}
}
the grid details context was set independently each and every time.

Related

Creating an autocomplete textbox with dropdown

My Problem:
I have a list of 118 chemical element names. And I wish to make a textbox in which as I type it will throw a dropdown menu with suggestion of names. I made this textbox in winforms and it was piece of cake however my efforts of making it in wpf are in vain. I've tried extended wpf toolkit, nimgoble and some other autocomplete textbox libs. So far dead end...I'm also new to wpf so maybe I'm missing something with these libs? I can't make them list my items and some won't even show the dropdown menu.
Heres what I wanted:
Here's how I finally achieved it:
So I solved this by using a combination of textbox and listbox. Where in textbox user types and as it changes (textbox changed event) it's checking for matches inside a list that holds names of all 118 elements and displays matches for the text typed in, inside listbox. Here's the code:
private void textBox_TextChanged(object sender, TextChangedEventArgs e)
{
listBox.Items.Clear();
if (textBox.Text.Trim() != "")
{
string regexPattern = (textBox.Text.ToString()) + "\\w*";
regexPattern = char.ToUpper(regexPattern[0]) + regexPattern.Substring(1); //prvo slovo veliko
Match match = Regex.Match(ElementNames.allElements, regexPattern);
while (match.Success && match.Value != "")
{
listBox.Items.Add(match.Value.ToString());
listBox.Visibility = Visibility.Visible;
match = match.NextMatch();
}
}
if (listBox.Items.IsEmpty || listBox.Items.Count == 119)
{
listBox.Visibility = Visibility.Collapsed;
if (listBox.Items.Count == 119) listBox.Items.Clear();
}
HighlightElementsOnTable();
OtherButtonsHighlight();
BringBackColors();
}
You can use a ComboBox with IsEditable=true.
Use a Combobox with
IsEditable = true
SelectedItem and Text data bound to the same property
StaysOpenOnEdit = true
<ComboBox ItemsSource="{Binding Models}" SelectedItem="{Binding Model}"
IsEditable="True" StaysOpenOnEdit="True" Text="{Binding Model, UpdateSourceTrigger=PropertyChanged}"/>

Object reference not set to an instance of an object when creating two ComboboxItems functions in wpf

I´m all out of ideas here
The thing is that Im using two comboboxes and I want to get values from both comboboxes to show content in DataGrid in wpf.
I have this function that gets values from both comboboxes. This works well.
private void cboxYearChange(object sender, SelectionChangedEventArgs e)
{
ComboBoxItem typeItemYear = (ComboBoxItem)comboBox2.SelectedItem;
string valueYear = typeItemYear.Content.ToString();
ComboBoxItem typeItemMonth = (ComboBoxItem)comboBox1.SelectedItem;
string valueMonth = typeItemMonth.Content.ToString();
}
But then I want to create another function to check for changes on the other combobox:
private void cboxMonthChange(object sender, SelectionChangedEventArgs e)
{
ComboBoxItem typeItemYear = (ComboBoxItem)comboBox2.SelectedItem;
string valueYear = typeItemYear.Content.ToString();
ComboBoxItem typeItemMonth = (ComboBoxItem)comboBox1.SelectedItem;
string valueMonth = typeItemMonth.Content.ToString();
}
I can build, but when I run this I get the Object reference not set to an instance of an object error on the ComboBoxItem typeItemYear = (ComboBoxItem)comboBox2.SelectedItem; line in the cboxMonthChange function
What am I missing here ?
SelectedItem is null until something is selected. Unless they both change at the same time (which is not possible as these events are fired in sequence), either the type cast on comboBox1.SelectedItem or comboBox2.SelectedItem will throw an exception.
Check if SelectedItem is set the methods.
Or use another cast, like:
ComboBoxItem item1 = comboBox1.SelectedItem as ComboBoxItem;
if (item1 != null)
{
// do something
}
Hope this helps :-)
1) you should not refer to control's name within the code whenever possible.
So you can know, for instance, which ComboBox was changed within a SelectionChanged
handler by casting the Sender to a ComboBox.
2) but in such a simple case, just use public properties and bind them to
your ComboBox : all will get done with no code.
<ComboBox x:Name="YearSelectCB" SelectedItem="{Binding SelectedYear}">
<ComboBox x:Name="MonthSelectCB" SelectedItem="{Binding SelectedMonth}">
(you can set the DataContext of the window in several ways, for instance in the
window loaded event handler (DataContext=this) )

New Item Placeholder in WPF DataGrid Disappears

I have an invalid new item row in my DataGrid. I change the focus to another element, which clears thee backing collection of this datagrid. This causes the new item placeholder (the empty row at the bottom of the datagrid) to disappear.
How do I get it to reappear?
If you're clearing the collection such that the DataGrid shows no rows at all, then it sounds like you're facing the same issue as discussed here:
WPF DataGrid: Blank Row Missing
Basically, the issue is that the DataGrid doesn't know what object type it is binding to, and therefore can't generate the columns for the new item placeholder (for example, it doesn't know whether to provide a blank cell for the name of a person, the delivery date of an order, or the hair colour of a pet, etc).
One of the answers in the discussion linked above is to add a dummy object then remove it - that worked for me.
I wasn't able to make the "delete item/reinsert item" fix work for my app. While troubleshooting this issue, I noticed the DataGrid's built-in DeleteCommand would 1) delete the selected row, even for a DataGrid with a bound ItemsSource and 2) not make the NewItemPlaceHolder row disappear if the DeleteCommand was fired on the row while it was in edit mode!
Here's my app's DataGrid, which has a DataGridTemplateColumn containing a button that uses the buil-in DeleteCommand.
Note: The row must be selected to enable the button, then clicking the button deletes the row. I'm sure you could subclass DataGrid's OnCanExecuteDelete and modify this behavior, however.
<local:MyDataGrid
ItemsSource="{Binding Companies}"
AutoGenerateColumns="False"
RowHeaderWidth="20"
x:Name="dg">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Delete">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="X"
Command="DataGrid.DeleteCommand"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
</DataGrid.Columns>
And as a bonus, here's the subclassed DataGrid that was giving me the same problem you're (or were - this is an old post) facing. It includes code I borrowed from another StackOverflow user that enables one click editing of any DataGridCell.
public class MyDataGrid : DataGrid
{
public MyDataGrid()
{
this.GotFocus += DataGrid_CellGotFocus;
}
protected override void OnExecutedDelete(ExecutedRoutedEventArgs e)
{
// Temporarily remove the GotFocus eventhandler to prevent
// the datagrid from firing BeginEdit after a row is deleted.
this.GotFocus -= DataGrid_CellGotFocus;
base.OnExecutedDelete(e);
this.GotFocus += DataGrid_CellGotFocus;
}
private void DataGrid_CellGotFocus(object sender, RoutedEventArgs e)
{
// Lookup for the source to be DataGridCell
if (e.OriginalSource.GetType() == typeof(DataGridCell))
{
// Starts the Edit on the row;
DataGrid grd = (DataGrid)sender;
grd.BeginEdit(e);
//Control control = e.OriginalSource as DataGridCell;
Control control = GetFirstChildByType<Control>(e.OriginalSource as DataGridCell);
if (control != null)
{
control.Focus();
}
}
}
/// <summary>
/// Returns the first visual tree child item matching T.
/// </summary>
/// <param name="prop"></param>
/// <returns></returns>
private T GetFirstChildByType<T>(DependencyObject prop) where T : DependencyObject
{
// Begin a loop thru all visual tree items for the DependencyObject arg.
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(prop); i++)
{
// Loop to the next visual tree item if the current item is null.
DependencyObject child = VisualTreeHelper.GetChild((prop), i) as DependencyObject;
if (child == null)
continue;
// Try casting the current item to T. If the cast works, return the visual tree
// item as T.
T castedProp = child as T;
if (castedProp != null)
return castedProp;
// If this code line is reached, the cast failed. Recursively call this method.
castedProp = GetFirstChildByType<T>(child);
if (castedProp != null)
return castedProp;
}
// If this code line is reached, no visual tree items in prop match T. Return null.
return null;
}
}

WPF DataTemplate: How to create a tooltip just-in-time?

I am using DataTemplates to render items in an ItemsControl. I want to show a tooltip for each item. If I use a binding such as ToolTip="{Binding MyToolTip,Mode=OneWay}", WPF gets the tooltip once at the beginning and does not update the tooltip when it is opened.
My items implement INotifyPropertyChanged, so in principle I could generate change notifications for MyToolTip. The problem is, the tooltip is produced based on many pieces of information in the underlying model. These pieces of information change frequently and it would be both cumbersome and inefficient to generate change notifications for the MyToolTip property. Besides, I do not want tooltips for ALL items to be produced initially. Instead I would like to force a fresh tooltip to be generated when the tooltip is opened. How can I do it?
You will have to use a little code-behind, but it isn't that bad.
<object ToolTip="{Binding MyToolTip, Mode=OneWay}" ToolTipOpening="MyToolTip_Opening" />
In code-behind
private void MyToolTip_Opening(object sender, ToolTipEventArgs e)
{
DependencyObject depObj = sender as DependencyObject;
if (depObj == null) return;
BindingExpression be = BindingOperations.GetBindingExpression(depObj, FrameworkElement.ToolTipProperty);
if (be != null) be.UpdateTarget();
}

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