Custom ComboBox control to defer current selection

I had this weird requirement in my project where:

When I change the selection, new value should be sent to ViewModel, but the selection should not be changed until Success response comes back from a custom device attached to the application.

i.e. If failure arrives then, combobox value will remain unchanged, and if success arrives then only ComboBox reflects the new value.

Requirement was very unusual and that was not the functionality a built in combo-box supposed to do. So, I had to create a custom control with few hacks to make it work.

Following is the code:

public class ExtendedComboBox : ComboBox, ICommandSource
{
private bool _valueUpdatedFromUI;
private int _previousIndex;
private bool _dropDownClosed;
private object _newlySelectedItem;

#region DP

/// <summary>
/// Sends new value to ViewModel property
/// </summary>
public object DeferredSelectedItem
{
get { return (object)GetValue(DeferredSelectedItemProperty); }
set { SetValue(DeferredSelectedItemProperty, value); }
}

/// <summary>
/// Using a DependencyProperty as the backing store for DeferredSelectedItem.  This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty DeferredSelectedItemProperty =
DependencyProperty.Register(nameof(DeferredSelectedItem), typeof(object), typeof(ExtendedComboBox), new UIPropertyMetadata(PropertyChanged));

#endregion

#region Methods

private static void PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue != e.NewValue)
((ExtendedComboBox)d).SelectedItem = e.NewValue;
}

/// <inheritdoc />
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
if (_valueUpdatedFromUI)
{
_valueUpdatedFromUI = false;

if (e.RemovedItems.Count > 0)
{
var indexOfPrev = Items.IndexOf(e.RemovedItems[0]);
_previousIndex = indexOfPrev;
}
if (e.AddedItems.Count > 0)
{
_newlySelectedItem = e.AddedItems[0];
}
}
else if (_dropDownClosed)
{
_dropDownClosed = false;
}
else
{
base.OnSelectionChanged(e);
}
}

/// <inheritdoc />
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
_valueUpdatedFromUI = true;
//if (IsDropDownOpen)
//{
// _previousIndex = SelectedIndex;
//}
}

/// <inheritdoc />
protected override void OnDropDownClosed(EventArgs e)
{
_dropDownClosed = true;
//base.OnDropDownClosed(e);

if (_newlySelectedItem != null)
{
//changing back DropDown selection
this.SelectedIndex = _previousIndex;

//Sends event to invoke ViewModel logic on combobox selection changed without actually changing Item on UI
Command.Execute(_newlySelectedItem);

_newlySelectedItem = null;
}
}

#endregion

#region Commands

/// <inheritdoc />
public System.Windows.Input.ICommand Command
{
get { return (System.Windows.Input.ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}

/// <summary>
/// 
/// </summary>
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(System.Windows.Input.ICommand), typeof(ExtendedComboBox), new UIPropertyMetadata(null));

/// <inheritdoc />
public object CommandParameter
{
get { return (object)GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}

// Using a DependencyProperty as the backing store for CommandParameter.  This enables animation, styling, binding, etc...
/// <summary>
/// 
/// </summary>
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register("CommandParameter", typeof(object), typeof(ExtendedComboBox), new UIPropertyMetadata(null));

/// <inheritdoc />
public IInputElement CommandTarget
{
get { return (IInputElement)GetValue(CommandTargetProperty); }
set { SetValue(CommandTargetProperty, value); }
}

/// <summary>
/// 
/// </summary>
public static readonly DependencyProperty CommandTargetProperty =
DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(ExtendedComboBox), new UIPropertyMetadata(null));

#endregion

}

Comments

Popular posts from this blog

How UI Layout Works

Binding Previous Item's data in ItemsControl