Is there a SelectionChanging event on a ListBox wpf? - c#

I would like a SelectionChanging event on a ListBox that I can cancel based on certain conditions...Is there a way to do this?
Thanks!

You may be able to get away with doing this inside the SelectedItem
Example:
private string _selectedItem;
public string SelectedItem
{
get { return _selectedItem; }
set
{
if (value != WhatIWant)
{
return;
}
_selectedItem = value;
}
}
This way if you dont like the value selected to can just bail, or change to somthing else.
DependancyProperites have a cool little callback (CoerceValueCallback) where you can change the value before it is propogated to the propertyChanged event.
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(string), typeof(MainWindow),
new UIPropertyMetadata(string.Empty,new PropertyChangedCallback(PropertyChanged),
new CoerceValueCallback(CoerceValue)));
private static void PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
private static object CoerceValue(DependencyObject d, object baseValue)
{
if (baseValue != WhatIWasExpecting)
{
return WhatIWant;
}
return baseValue;
}

No, there is no event in the stock WPF ListBox control that fires before changing the selection that allows you to cancel the change.
However, the SelectionChangedEventArgs class, which you receive during a SelectionChangedEvent, provides you both the old value (being deselected) and the new value (being selected). You could apply your logic in that event and reset the selection back if it fails.
I think the user might notice the flicker of the selection jumping around, if you take this approach, but AFAIK it's the best you can do without subclassing the control and implementing your own SelectionChanging event.

Related

WPF : MvvmLight, Usercontrol, Binding

I have a simple usercontrol (DoubleRadioControl2), composed of 2 radio buttons. I have a Dep Prop on this UC : (bool?)IsOuiChecked :
true - 'yes' radio checked
false - 'no' radio checked
null - both
radios unchecked
Pretty simple.
private static readonly DependencyProperty IsOuiCheckedProperty = DependencyProperty.Register("IsOuiChecked", typeof(bool?), typeof(DoubleRadioControl2), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public bool? IsOuiChecked
{
get
{
return (bool)GetValue(IsOuiCheckedProperty);
}
set
{
SetValue(IsOuiCheckedProperty, value);
if (value == null)
{
RadioYes.IsChecked = false;
RadioNo.IsChecked = false;
}
else
{
RadioYes.IsChecked = (bool) value;
RadioNo.IsChecked = !(bool) value;
}
}
}
And the logic between ths 2 radios :
private void OptDecompteConfnOui_Click(object sender, RoutedEventArgs e)
{
IsOuiChecked = true;
}
private void OptDecompteConfnNon_Click(object sender, RoutedEventArgs e)
{
IsOuiChecked = false;
}
When i bind this UC to my viewmodel, the prop binded if updated when i click on my radios. But when i set the value in the code of my view model, it won't update my radios.
In my viewmodel :
private bool? _isDRB2OuiChecked;
public bool? IsDRB2OuiChecked
{
get
{
return _isDRB2OuiChecked;
}
set
{
if (_isDRB2OuiChecked == value)
{
return;
}
_isDRB2OuiChecked = value;
RaisePropertyChanged(() => IsDRB2OuiChecked);
}
}
TwoWay binding not working.
The getters and setters for your dependency property only exist for your (the programmers) convenience. WPF itself will not call them, but set the property directly.
You need to attach a handler to the changed event of the property. Use one of the FrameworkPropertyMetadata constructors, that take a PropertyChangedCallback . Your getter and setter logic needs to be handled there instead of inside the property.
If you are using MVVM you should call OnPropertyChanged
First of all this is a very BAD practice to put code inside the Get/Set parts of a dependency property, look at these exanples to get the solution. If you have the logic on DP changes, you have to put that login inside the callback.
Dependency properties explanation here.
MSDN Overview here.
if there is binding defined at the View/Viewmodel level you have to use a INotifyPropertyChange event at the Viewmodel side to inform View that the binded property was changed.
regards,

Two-way data binding of text box focus with DependencyProperty

In a WPF application built by the MVVM principle, I need to explicitly set the focus to a TextBox from code (reacting on a keyboard event), and also know whether focus has been lost. From another question here I have gathered that appearently the way to do this is with data binding on a DependencyProperty. For that I have picked up the following code:
public static class FocusHelper
{
public static bool GetIsFocused(DependencyObject obj)
{
return (bool)obj.GetValue(IsFocusedProperty);
}
public static void SetIsFocused(DependencyObject obj, bool value)
{
obj.SetValue(IsFocusedProperty, value);
}
public static readonly DependencyProperty IsFocusedProperty =
DependencyProperty.RegisterAttached(
"IsFocused", typeof(bool), typeof(FocusHelper),
new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));
private static void OnIsFocusedPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var uie = (UIElement)d;
if ((bool)e.NewValue)
{
uie.Focus();
}
}
}
The TextBox binding looks like this:
<TextBox Text="{Binding Path=RowData.Row.Group}" helper:FocusHelper.IsFocused="{Binding RowData.Row.GroupFocused, Mode=TwoWay}"
This is in a (DevExpress) grid; Row is the actual ViewModel the entire row is bound to.
The corresponding code in the bound ViewModel:
private bool groupFocused;
public bool GroupFocused
{
get { return this.groupFocused; }
set
{
this.groupFocused = value;
this.NotifyOfPropertyChange(() => this.GroupFocused);
}
}
When the keyboard event is handled, GroupFocused is set to true, which sure enough invokes the DependencyProperty callback and sets the focus to the TextBox. When the control loses focus, however, that change is not reflected back on the ViewModel (the DependencyProperty's callback is also not invoked).
Is there any obvious reason for this behavior? Anything wrong with the code?
(I have tried adding UpdateSourceTrigger with both PropertyChanged and LostFocus to the binding, also changing the DependencyProperty's default value to true, none of which changed anything about the behavior. I also tried IsKeyboardFocused instead of IsFocused with no change, and IsKeyboardFocusWithin, which made the application close with no comment - likely a DevExpress exception - on assembling the grid.)
You are not data binding your GroupFocused property to the TextBox.IsFocused property, you are data binding it to the FocusHelper.IsFocused property, which programmatically calls Focus on the TextBox.
There's a distinct difference. Calling the Focus method on the TextBox will result in the TextBox.IsFocused property being set to true, but there is no connection between that property and either your GroupFocused or your FocusHelper.IsFocused properties, so neither will be set to false when the TextBox.IsFocused property is set to false.
Furthermore, you cannot data bind to the TextBox.IsFocused property in an attempt to set the focus because it is read only. See the UIElement.IsFocused Property page on MSDN for more information on that.
However, there is a simple solution. It might not look pretty, but it will certainly work... just set the GroupFocused property to false before you set it to true each time. Perhaps something like this:
private void FocusTextBox()
{
GroupFocused = false;
GroupFocused = true;
}
...
FocusTextBox();

Binding - callback is called twice

I've a simple control with dependency property like this
public class StatusProgress : Control
{
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(StatusProgress),
new FrameworkPropertyMetadata(null, (d, e) => (d as StatusProgress).TextUpdated(e.OldValue as string)));
private void TextUpdated(string text)
{
Trace.WriteLine("test");
}
}
then I have view model
public class ViewModelPageAnalyse : ViewModelPageBase
{
private string _progressText;
public string ProgressText
{
get { return _progressText; }
set
{
_progressText = value;
OnPropertyChanged(); // base class INotifyPropertyChanged implementation
}
}
}
Then there is a user control (displayed in window with ContentControl). User control is bound to view model with data template (maybe this is important)
<DataTemplate DataType="{x:Type local:ViewModelPageAnalyse}">
<local:UserControlAnalyse/>
</DataTemplate>
And this is the control in user control
<local:StatusProgress Text="{Binding ProgressText}"/>
Now the interesting part. When ProgressText in view model is set/changed, property changed callback is called twice. I see twice "test" in the debugger output window.
More interesting: when view is changed, for some reason callback is again called with e.NewValue = null, while there is nothing directly sets ProgressText to null.
I tried already to check if value is changed in the ProgressText setter before rising event, tried to set binding mode one-way, problem still - callback is called twice with same value, call stack looks same, but there are really a lot of calls within wpf to be really sure.
While double-shot is not a real issue, it bother me. Callback with null value is what my real problem (I think they are related). Anyone knows what is wrong?
Found a reason of the first problem: it was other control with Content. During transition it created a new Model (because Content is ViewModel) instead of reassigning existing user control. Totally my fault. Second problem still and I found this question (with workaround which is not suiting me).
Need help with
PropertyChanged callback is called with default value when ContentControl ViewModel is changed.
Which means null for Text in my case. Anyone? I couldn't figure out why is it called. My guess it is called by DataTemplate manager, if I can say so.
try to change this line:
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(StatusProgress),
new FrameworkPropertyMetadata(null, (d, e) => (d as StatusProgress).TextUpdated(e.OldValue as string)));
with this:
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(StatusProgress)
, new PropertyMetadata(""));

unable to use two way Binding with my control

I am new to binding. I have binded slider value to my control's property and my controls property get changed when I change the slider value.
Now, when I need to change the slider value by changing my property value, it does not work..
I modified the xaml from some internet source, but still not get the expected output.
can anyone help me out...
<Grid>
<cc:MyControl Name="mycntrl" ZoomPercentage="{Binding ElementName=slider,Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></cc:MyControl>
<Slider Name="slider" Margin="20,20,20,400" Minimum="100" Maximum="400"></Slider>
</Grid>
Updated:
My code behind for my ZoomPercentage dependency property is below
public double ZoomPercentage
{
get
{
return (double)GetValue(ZoomPercentageProperty);
}
set
{
SetValue(ZoomPercentageProperty, value);
}
}
My dependency registration
public static readonly DependencyProperty ZoomPercentageProperty = DependencyProperty.Register("ZoomPercentage", typeof(double), typeof(MyControl), new FrameworkPropertyMetadata(ZoomPercentagePropertyChanged));
public static void ZoomPercentagePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (args.OldValue != null)
{
if ((double)args.NewValue != (double)args.OldValue)
{
MyControl mycontrol = obj as MyControl;
mycontrol .ZoomTo((int)((double)args.NewValue));
}
}
}
Your ZoomPercentage property should be implemented as a Dependencyproperty
Something like this
public class MyControl:UserControl
{
public MyControl() : base() { }
public double ZoomPercentage
{
get { return (double)this.GetValue(ZoomPercentageProperty); }
set { this.SetValue(ZoomPercentageProperty, value); }
}
public static readonly DependencyProperty ZoomPercentageProperty = DependencyProperty.Register(
"ZoomPercentage", typeof(double), typeof(MyControl:),new PropertyMetadata(0));
}
read more here
If you want a data bound control in the UI to update after changes made in code then you have to do one of two things. One option is to correctly implement the INotifyPropertyChanged interface in the class that you declared your Value property.
The other is to declare your Value property as a DependencyProperty, although you should only really do this in the code behind of your Window or UserControl and opt for the first method if you are using a view model. The purpose of these two methods is for you to 'plug in' to WPF notification framework, so that your UI control will update. Please read the linked pages for more information.

WPF Grid and focus of controls inside it

I have MyContainer User Control with Grid inside. Each cell of this Grid contains a some control derived from MyControlBase class. These controls are added dynamically.
I need to implement a FocusedControl bound property in MyContainer to get currently focused or set focus to any of MyControlBase children. I know about FocusManager.FocusedElement but haven't ideas how to implement it properly.
Dunno if this helps your situation, or if it's even the right way to go about mine, but in a pinch I've found that listening for the Loaded event on the object I want to focus and then using Dispatcher.BeginInvoke to send a Focus request to my control on the Background or ApplicationIdle priority has worked when other methods haven't. For example:
private void MyControlLoaded(object sender, RoutedEventArgs e)
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action( () => MyControl.Focus() ));
}
OK, I found how to do it myself.
First define our new dependence property FocusedAdapterProperty as usual:
public static readonly DependencyProperty FocusedAdapterProperty;
static SpreadGridControl()
{
FocusedAdapterProperty = DependencyProperty.Register("FocusedAdapter",
typeof(object), typeof(SpreadGridControl),
new FrameworkPropertyMetadata(null, null));
}
public object FocusedAdapter
{
get { return GetValue(FocusedAdapterProperty); }
set { SetValue(FocusedAdapterProperty, value); }
}
Next add GotFocus handler to parent container e.g. <Grid GotFocus="Grid_OnGotFocus">
Check the e.OriginalSource and search most common ancestor of required type and set property to new value:
private void Grid_OnGotFocus(object sender, RoutedEventArgs e)
{
var control = UIHelpers.TryFindParent<ControlBase>
((DependencyObject)e.OriginalSource);
if (control != null)
FocusedAdapter = control.Adapter;
}
The implementation of TryFindParent can be found here

Categories