I have a combobox of type List. I have the ItemsSource and the ItemSelected bound through the datacontext. If the selected item has been changed then I show a pop up message confirming the users action. On clicking of 'Ok' the selection gets changed. But on clicking of cancel, the selection should be cancelled and previous item should be retained. Below is the property that is bound to SelectedItem of the combobox.
Public SomeClass Sel
{
get
{
return _sel;
}
set
{
if (_sel != value)
{
var sview = _sel;
if (Compare())
{
_sel = value;
if (Sel != null)
IsDefault = Sel.IsDefault;
OnPropertyChanged(() => Sel);
}
else
{
MessageBoxResult result = MessageBox.Show("Message.", "Owb Message", MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK)
{
_sel = value;
if (Sel != null)
IsDefault = Sel.IsDefault;
OnPropertyChanged(() => Sel);
}
else
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
_sel = sview;
OnPropertyChanged("Sel");
}), DispatcherPriority.Send, null);
return;
}
}
}
}
}
The combo box is in a pop window. So would Dispatcher object work in that case?
I'm guessing the selected value is retained, but the View doesn't update correctly.
Have a look at this article: http://www.codeproject.com/Articles/407550/The-Perils-of-Canceling-WPF-ComboBox-Selection. Basically, the few workarounds that did exist in .Net 3.5 no longer work in .Net 4.0..
As a general rule, if you've got visual controls leaking into your viewmodel, you're going down a path you don't want to go down.
Create a Behavior that intercepts the OnChanged event of the ComboBox and launches a message box. Here's a tutorial on using behaviours
This keeps all the UI logic in the UI and leaves your viewmodel to manage data and validation.
It works like magic now! I missed out setting the value before calling dispatcher.
_sel = sview
Related
I have datagridview on winform. It is bound to result from below code:
PoolEntities db = new PoolEntities();
var Result = db.General_Pool_Detail.Where(g => g.Pool_Name == cbxGLType.SelectedValue && g.Mapped_Date == dt).Select(s=>
new { Selected = true, s.Gen_Pool_ID, s.GSL_Code, s.Amount }).ToList();
dgvGeneralPoolData.DataSource = Result;
The code works perfectly fine. But when I uncheck the checkbox on datagridview it does not work.
In datagirdview event i have written the following code:
private void dgvGeneralPoolData_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (dgvGeneralPoolData.Rows[e.RowIndex].Cells[0].Selected)
{
Boolean IsChecked = (Boolean) dgvGeneralPoolData.Rows[e.RowIndex].Cells[0].Value;
if (IsChecked)
{
dgvGeneralPoolData.Rows[e.RowIndex].Cells[0].Value = false;
}
}
}
Also, I want to save the changes made in datagridview to the database.
Please help.
EditMode Property
check editmode property of your datagridview.
Does it even hit your click event? Where exactly does it break when you debug? Try to debug and provide some more info if you can.
1) Debug and make sure that the event handler does work. Sometimes when you copy paste the code the event handler doesn't get register by the designer so try deleting the CellClick and type the code again on the grid view, when you add the = sign it should give you an option to create the ClickCell method for you. It should be something like this inside your click event.
if (e.RowIndex != -1)
{
DataGridViewCheckBoxCell chk = (DataGridViewCheckBoxCell)dgvGeneralPoolData.CurrentRow.Cells["ColumnNumberHere"];
if (chk.Value == null || chk.Value = false)
{
chk.Value = true;
}
else
{
chk.Value = false
}
}
2) Make sure this is under your InitializeComponent()
this.dgvGeneralPoolData.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgvGeneralPoolData_CellContentClick);
I am working with WPF DataGrid in the MVVM manner and having trouble reverting the selection change from the ViewModel.
Is there any proven way to do this? My latest tried out code is below. Now I do not even mind investing on a hack inside the code behind.
public SearchResult SelectedSearchResult
{
get { return _selectedSearchResult; }
set
{
if (value != _selectedSearchResult)
{
var originalValue = _selectedSearchResult != null ? _selectedSearchResult.Copy() : null;
_selectedSearchResult = value;
if (!DispatchSelectionChange(value)) // Returns false if the selection has to be cancelled.
{
_selectedSearchResult = originalValue;
// Invokes the property change asynchronously to revert the selection.
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(() => NotifyOfPropertyChange(() => SelectedSearchResult)));
return;
}
NotifyOfPropertyChange(() => SelectedSearchResult);
}
}
}
After days of trial and error, finally got it working. Following is the code:
public ActorSearchResultDto SelectedSearchResult
{
get { return _selectedSearchResult; }
set
{
if (value != _selectedSearchResult)
{
var originalSelectionId = _selectedSearchResult != null ? _selectedSearchResult.Id : 0;
_selectedSearchResult = value;
if (!DispatchSelectionChange(value)) // Returns false if the selection has to be cancelled.
{
// Invokes the property change asynchronously to revert the selection.
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(() => RevertSelection(originalSelectionId)));
return;
}
NotifyOfPropertyChange(() => SelectedSearchResult);
}
}
}
private void RevertSelection(int originalSelectionId)
{
_selectedSearchResult = SearchResults.FirstOrDefault(s => s.Id == originalSelectionId);
NotifyOfPropertyChange(() => SelectedSearchResult);
}
Key here is to use a brand new originally selected item from the databound grid's collection (ie: SearchResults) rather than using a copy of the selected item. It looks obvious, but took days for me to figure it out! Thanks for everyone who helped :)
if you want to prevent the selection change you can try this.
if you want to revert a selection, you can just use ICollectionView.MoveCurrentTo() methods (at least you must have to know what item you want to select ;)).
its not quite clear to me what you really want.
I have a checkbox with its IsChecked property bound to a nullable bool. When my control first loads the value is null and the checkbox appears greyed out. This is what I want.
When the user clicks the checkbox, it moves to the false/Unchecked state.
However, 99% of the time the user is going to want to tick the checkbox - which currently means double clicking the checkbox.
How can I make the value move from null to true when the user first clicks the checkbox?
I had the same problem and ran into this question. This is a late response but I think this is the best solution :)
With the help of IsThreeState and TargetNullValue you can accomplish this
<CheckBox IsThreeState="False" IsChecked="{Binding YOUR_NULLABLE_PROPERTY, TargetNullValue=false}" />
The only caveat is that it will toggle between null and true. There will never be a false value.
You can just modify the setter of the bound property to check whether the previous value is null and if it is, set the value to true. Something like this:
public bool? MyBoolProperty
{
get { return _myBoolProperty; }
set
{
_myBoolProperty = (_myBoolProperty != null || value == null) ? value : true;
RaisePropertyChanged("MyBoolProperty");
}
}
The binding system will re-read the property after it sets it, so the new value will be reflected by the CheckBox.
You can handle the Click event and implement a logic like this:
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
CheckBox cb = sender as CheckBox;
switch (cb.IsChecked)
{
case null:
cb.IsChecked = false;
break;
case true:
cb.IsChecked = true;
break;
case false:
if (cb.IsThreeState) {
cb.IsChecked = null;
} else {
cb.IsChecked = true;
}
break;
}
e.Handled = true;
}
I have met this problem recently. I look all answers of this question, but none seem fit me. The accepted answer is fail when I set IsChecked equals false in ViewModel. I decompile the checkbox in wpf.
As you can see, when you click a null IsChecked checkbox, IsChecked property will switch to false.
protected internal virtual void OnToggle()
{
bool? flag;
if (this.IsChecked == true)
{
flag = (this.IsThreeState ? null : new bool?(false));
}
else
{
flag = new bool?(this.IsChecked.HasValue);
}
base.SetCurrentValueInternal(ToggleButton.IsCheckedProperty, flag);
}
So you can Create a new class inherit CheckBox and override OnToggle method like this:
protected override void OnToggle()
{
bool? flag;
if (this.IsChecked == true)
{
flag = this.IsThreeState ? null : new bool?(false);
}
else
flag = true;
// what a pity this method is internal
// actually you can call this method by reflection
base.SetCurrentValueInternal(ToggleButton.IsCheckedProperty, flag);
}
The other way is:
protected override void OnToggle()
{
if (this.IsChecked == null)
this.IsChecked = false;
base.OnToggle();
}
The easiest way would be to simply handle the click event and set the control to true if its current state is null, optionally setting a flag for your internal code tracking after first click.
I'm using a MVVM approach with WPF to let the user select one item in a combobox. The model contains a set of possible options, the combobox is bound to this set, the current selection is again bound to a property of my model. This part works fine.
Now I'd like to allow the user to enter an arbitrary text into the combobox. If the text doesn't correspond to an existing item the program should ask him if he wants to add a new item. He should also be allowed to cancel the action and select another item.
How would I do that within the MVVM pattern?
You would check the "already existing" status of the text from your ViewModel's bound property setter. At that point, you need a mechanism to raise an event and decide what to do based on what happens.
An example:
enum Outcome { Add, Cancel }
class BlahEventArgs : EventArgs
{
Outcome Outcome { get; set; }
}
class ViewModel
{
private string name;
public EventHandler<BlahEventArgs> NotExistingNameSet;
public Name
{
get { return this.name; }
set
{
if (/* value is existing */) {
this.name = value;
return;
}
var handler = this.NotExistingNameSet;
if (handler == null) {
// you can't just return here, because the UI
// will desync from the data model.
throw new ArgumentOutOfRangeException("value");
}
var e = new BlahEventArgs { Outcome = Outcome.Add };
handler(this, e);
switch (e.Outcome) {
case Outcome.Add:
// Add the new data
this.name = value;
break;
case Outcome.Cancel:
throw new Exception("Cancelled property set");
}
}
}
}
Your View would add an event handler to NotExistingNameSet to present appropriate UI and set the value of e.Outcome accordingly.
The datatemplate for the ListBox is set dynamically by XamlReader.Load. I am subscribing to Checked event by getting the CheckBox object using VisualTreeHelper.GetChild. This event is not getting fired
Code Snippet
public void SetListBox()
{
lstBox.ItemTemplate =
XamlReader.Load(#"<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' x:Name=""DropDownTemplate""><Grid x:Name='RootElement'><CheckBox x:Name='ChkList' Content='{Binding " + TextContent + "}' IsChecked='{Binding " + BindValue + ", Mode=TwoWay}'/></Grid></DataTemplate>") as DataTemplate;
CheckBox chkList = (CheckBox)GetChildObject((DependencyObject)_lstBox.ItemTemplate.LoadContent(), "ChkList");
chkList.Checked += delegate { SetSelectedItemText(); };
}
public CheckBox GetChildObject(DependencyObject obj, string name)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject c = VisualTreeHelper.GetChild(obj, i);
if (c.GetType().Equals(typeof(CheckBox)) && (String.IsNullOrEmpty(name) || ((FrameworkElement)c).Name == name))
{
return (CheckBox)c;
}
DependencyObject gc = GetChildObject(c, name);
if (gc != null)
return (CheckBox)gc;
}
return null;
}
How to handle the checked event? Please help
You need to understand the reason why ItemTemplate is a DataTemplate. For each item the the list box needs to display it will call the LoadContent() method. This create a new instance of the content described including, in this case, a new checkbox. All this then gets bound to the item when it is assigned as the Content of a ListBoxItem.
All the instances of checkbox in this case are independent objects. All you have done is created yet another independent instance which is not used anywhere in the actual UI and attached an event handler to it. None of the checkboxes for the items in the list will share this handler hence the event code is never called.
Removed ItemTemplate and added the below code
var checkBox = new CheckBox { DataContext = item };
if (string.IsNullOrEmpty(TextContent)) checkBox.Content = item.ToString();
else
checkBox.SetBinding(ContentControl.ContentProperty,
new Binding(TextContent) { Mode = BindingMode.OneWay });
if (!string.IsNullOrEmpty(BindValue))
checkBox.SetBinding(ToggleButton.IsCheckedProperty,
new Binding(BindValue) { Mode = BindingMode.TwoWay });
checkBox.SetBinding(IsEnabledProperty, new Binding("IsEnabled") { Mode = BindingMode.OneWay });
checkBox.Checked += (sender, RoutedEventArgs) => { SetSelectedItemText(true, ((CheckBox)sender).GetValue(CheckBox.ContentProperty).ToString()); };
checkBox.Unchecked += (sender, RoutedEventArgs) => { SetSelectedItemText(true, ((CheckBox)sender).GetValue(CheckBox.ContentProperty).ToString()); };
This fixed the issue