I have a UserControl that displays a ComboBox along with add and edit buttons.
XAML Code Block
<Style TargetType="local:MaintenanceComboBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MaintenanceComboBox">
<Border x:Name="_boxBorder">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ComboBox x:Name="cboMaintenance" Grid.Column="0"
Background="{StaticResource BH_TitleDefaultBackground}" Foreground="Black"
VerticalContentAlignment="Center"
ItemsSource="{TemplateBinding DataSource}"
DisplayMemberPath="{TemplateBinding DisplayItem}"
SelectedValue="{TemplateBinding ModelPropertyField}"
SelectedValuePath="{TemplateBinding DisplayRecordKey}"/>
<Button Grid.Column="1" Name="btnAddRecord" ToolTip="Add Record" Style="{StaticResource IconButton}" Margin="1.5,0" Background="Transparent"
Command="{TemplateBinding AddRecordCommand}">
<Image Source="{Binding Converter={StaticResource ImgConverter}, ConverterParameter={x:Static utilui:eImages_16x16.Add} }"/>
</Button>
<Button Grid.Column="2" Name="btnEditRecord" ToolTip="Edit Record" Margin="1.5,0" Background="Transparent"
Command="{TemplateBinding EditRecordCommand}">
<Image Source="{Binding Converter={StaticResource ImgConverter}, ConverterParameter={x:Static utilui:eImages_16x16.PageEdit} }"/>
<Button.Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource IconButton}" >
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=cboMaintenance, Path=SelectedIndex}" Value="0">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Content Control Code
public class MaintenanceComboBox : ContentControl
{
private static object _previousSelection = null;
public ICommand AddRecordCommand
{
get { return (ICommand)GetValue(AddRecordCommandProperty); }
set { SetValue(AddRecordCommandProperty, value); }
}
public static readonly DependencyProperty AddRecordCommandProperty =
DependencyProperty.Register("AddRecordCommand", typeof(ICommand), typeof(MaintenanceComboBox),
new PropertyMetadata(default(ICommand), new PropertyChangedCallback(AddRecordCommandChanged)));
private static void AddRecordCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MaintenanceComboBox)d).AddRecordCommand = (ICommand)e.NewValue;
}
public ICommand EditRecordCommand
{
get { return (ICommand)GetValue(EditRecordCommandProperty); }
set { SetValue(EditRecordCommandProperty, value); }
}
public static readonly DependencyProperty EditRecordCommandProperty =
DependencyProperty.Register("EditRecordCommand", typeof(ICommand), typeof(MaintenanceComboBox),
new PropertyMetadata(default(ICommand), new PropertyChangedCallback(EditRecordCommandChanged)));
private static void EditRecordCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MaintenanceComboBox)d).EditRecordCommand = (ICommand)e.NewValue;
}
//------------------------------------------------------------------------------------------
public Color BackgroundColor
{
get { return (Color)GetValue(BackgroundColorProperty); }
set { SetValue(BackgroundColorProperty, value); }
}
public static readonly DependencyProperty BackgroundColorProperty =
DependencyProperty.Register("BackgroundColor", typeof(Color), typeof(MaintenanceComboBox),
new PropertyMetadata(default(Color), new PropertyChangedCallback(BackgroundColorChanged)));
private static void BackgroundColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MaintenanceComboBox)d).BackgroundColor = (Color)e.NewValue;
}
//------------------------------------------------------------------------------------------
public object DataSource
{
get { return GetValue(DataSourceProperty); }
set { SetValue(DataSourceProperty, value); }
}
public static readonly DependencyProperty DataSourceProperty =
DependencyProperty.Register("DataSource", typeof(object), typeof(MaintenanceComboBox),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, DataSourceChanged));
private static void DataSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MaintenanceComboBox)d).DataSource = e.NewValue;
}
//------------------------------------------------------------------------------------------
public string DisplayItem
{
get { return GetValue(DisplayItemProperty).ToString(); }
set { SetValue(DisplayItemProperty, value); }
}
public static readonly DependencyProperty DisplayItemProperty =
DependencyProperty.Register("DisplayItem", typeof(string), typeof(MaintenanceComboBox),
new PropertyMetadata(default(string), new PropertyChangedCallback(DisplayItemChanged)));
private static void DisplayItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MaintenanceComboBox)d).DisplayItem = e.NewValue.ToString();
}
//------------------------------------------------------------------------------------------
public string DisplayRecordKey
{
get { return GetValue(DisplayRecordKeyProperty).ToString(); }
set { SetValue(DisplayRecordKeyProperty, value); }
}
public static readonly DependencyProperty DisplayRecordKeyProperty =
DependencyProperty.Register("DisplayRecordKey", typeof(string), typeof(MaintenanceComboBox),
new PropertyMetadata(default(string), new PropertyChangedCallback(DisplayRecordKeyChanged)));
private static void DisplayRecordKeyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MaintenanceComboBox)d).DisplayRecordKey = e.NewValue.ToString();
}
//------------------------------------------------------------------------------------------
public object ModelPropertyField
{
get { return GetValue(ModelPropertyFieldProperty); }
set { SetValue(ModelPropertyFieldProperty, value); }
}
public static readonly DependencyProperty ModelPropertyFieldProperty =
DependencyProperty.Register("ModelPropertyField", typeof(object), typeof(MaintenanceComboBox),
new PropertyMetadata(default(object), new PropertyChangedCallback(ModelPropertyFieldChanged)));
private static void ModelPropertyFieldChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MaintenanceComboBox)d).ModelPropertyField = e.NewValue;
}
//------------------------------------------------------------------------------------------
public int SelectedIndex
{
get { return Convert.ToInt32(GetValue(SelectedIndexProperty)); }
set { SetValue(SelectedIndexProperty, value); }
}
public static readonly DependencyProperty SelectedIndexProperty =
DependencyProperty.Register("SelectedIndex", typeof(int), typeof(MaintenanceComboBox),
new PropertyMetadata(default(int), new PropertyChangedCallback(SelectedIndexChanged)));
private static void SelectedIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MaintenanceComboBox)d).SelectedIndex = Convert.ToInt32(e.NewValue);
}
//------------------------------------------------------------------------------------------
public object ModelSelectionItem
{
get { return GetValue(ModelSelectionItemProperty); }
set { SetValue(ModelSelectionItemProperty, value); }
}
public static readonly DependencyProperty ModelSelectionItemProperty =
DependencyProperty.Register("ModelSelectionItem", typeof(object), typeof(MaintenanceComboBox),
new PropertyMetadata(default(object), new PropertyChangedCallback(ModelSelectionItemChanged)));
private static void ModelSelectionItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MaintenanceComboBox)d).ModelSelectionItem = e.NewValue;
}
}
The maintenance box is used multiple times on a form to choose from a shared list of business contacts for separate business roles. All uses of the user control can add or edit an entry. When this occurs, the data will be refreshed in order to allow a selection.
Usage of the Maintenance ComboBox Control
<utilctl:MaintenanceComboBox x:Name="cboFieldService" DisplayItem="FullName" DisplayRecordKey="ActorID"
ModelPropertyField="{Binding FieldServiceRep, Mode=TwoWay}"
DataSource="{Binding ContactList, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
Upon the initial display of a record, everything functions as expected:
- contact list is loaded in all maintenance box controls
- current business role selections are displayed as the selected item.
After the add or edit record process is completed and the data refreshed, the current item selected lost while the the dropdown list displays the updated contacts list.
A view model is the DataContext for the form that feeds the maintenance controls and implements INotifyPropertyChanged. The form is rather large and is pretty repetitive functionality.
Property definition for the cboFieldService
private string _fieldServiceRep = string.Empty;
public string FieldServiceRep { get { return _fieldServiceRep; } set { _fieldServiceRep = value; OnPropertyChanged(); } }
While researching this the past few days, there have been numerous mentions to:
- INotifyPropertyChanged - is being implemented
- IsSyncronizeWithCurrentItem - can not be set to true since the uses of the user control are all independent selections that happen to share the same contacts selection pool
Debugging the past few days indicates that the property values of the individual contact selections are still intact, however they aren't displayed after refresh. What can be done so that the each list selection properly displays after the contacts list has been refreshed following an add or edit?
UPDATE # 1------
When the record is loaded, the add and edit commands are defined in the view model.
public void LoadRecord(IncidentModel record)
{
DataContext = _activeIncident;
cboFieldService.AddRecordCommand = new DelegateCommand(_activeIncident.AddFieldServiceRep);
cboFieldService.EditRecordCommand = new DelegateCommand(_activeIncident.EditFieldServiceRep);
}
public void AddFieldServiceRep() { AddContact(nameof(FieldServiceRep)); }
public void AddContact(string propertyName)
{
ProcessContactMaintenance(new ContactMaintenanceWindow(MaintenanceType.Add), propertyName);
}
public void EditFieldServiceRep() { EditContact(ContactTypeCode.Rep, nameof(FieldServiceRep)); }
public void EditContact(ContactTypeCode type, string propertyName)
{
ProcessContactMaintenance(new ContactMaintenanceWindow(MaintenanceType.Edit, GetIncidentContact(type)?.ActorInfo), propertyName);
}
private void ProcessContactMaintenance(ContactMaintenanceWindow win, string propertyName)
{
win.ShowDialog();
if (win.DialogResult.Equals(true))
{
RefreshContactList(); OnPropertyChanged(nameof(ContactList)); UpdateContactProperties();
}
}
public void RefreshContactList()
{
ObservableCollection<ActorModel> contacts = new ObservableCollection<ActorModel>(FieldServiceManagementDataService.GetActors());
List<ActorModel> actors = contacts?.OrderBy(c => c.FullName).ToList();
ActorModel none = new ActorModel();
none.ActorID = "-1";
none.FirstName = "-- Select --";
actors?.Insert(0, none);
ContactList = new ObservableCollection<ActorModel>(actors);
}
public void UpdateContactProperties()
{
OnPropertyChanged(nameof(FieldServiceRep));
}
UPDATE # 2 ------
Per recommendation, the DataSource was redefined and the reference to the PropertyChangedCallback was removed. The result was unchanged behavior.
The behavior occurs only when the ContactList binding mode=TwoWay. When it is set to OneWay, the ComboBox selections do not show any updated information and the record selections are visually shown.
public ObservableCollection<dynamic> DataSource
{
get { return (ObservableCollection<dynamic>)GetValue(DataSourceProperty); }
set { SetValue(DataSourceProperty, value); }
}
public static readonly DependencyProperty DataSourceProperty =
DependencyProperty.Register("DataSource", typeof(ObservableCollection<dynamic>), typeof(MaintenanceComboBox),
new PropertyMetadata(default(ObservableCollection<dynamic>)));
Before Image
Image after contact information is updated (Mode=TwoWay)