ComboBox.SourceUpdated event is not fired - c#

I have two ComboBoxes on my view. Both of them are bound to two different ObservableCollections in the ViewModel, and when the selected item in ComboBox1 is changed, ComboBox2 gets updated with a different collection. The binding works just fine, however, I want the second ComboBox to always select the first item in its collection. Initially, it works, however, when the source and items in ComboBox2 get updated, the selection index gets changed to -1 (i.e. the first item is no longer selected).
To fix this, I added a SourceUpdated event to ComboBox2 and method that the event calls changes the index back to 0. The problem is that the method is never called (I put a breakpoint at the very top of the method and it doesn't get hit). Here's my XAML code:
<Grid>
<StackPanel DataContext="{StaticResource mainModel}" Orientation="Vertical">
<ComboBox ItemsSource="{Binding Path=FieldList}" DisplayMemberPath="FieldName"
IsSynchronizedWithCurrentItem="True"/>
<ComboBox Name="cmbSelector" Margin="0,10,0,0"
ItemsSource="{Binding Path=CurrentSelectorList, NotifyOnSourceUpdated=True}"
SourceUpdated="cmbSelector_SourceUpdated">
</ComboBox>
</StackPanel>
</Grid>
And in the code-behind:
// This never gets called
private void cmbSelector_SourceUpdated(object sender, DataTransferEventArgs e)
{
if (cmbSelector.HasItems)
{
cmbSelector.SelectedIndex = 0;
}
}
Any help is appreciated.

After working on it for an hour I finally figured it out. The answer is based on this question: Listen to changes of dependency property.
So basically you can define a "Property Changed" event for any DependencyProperty on an object. This can be extremely helpful when you need to extend or add additional events to a control without having to create a new type. The basic procedure is like this:
DependencyPropertyDescriptor descriptor =
DependencyPropertyDescriptor.FromProperty(ComboBox.ItemsSourceProperty, typeof(ComboBox));
descriptor.AddValueChanged(myComboBox, (sender, e) =>
{
myComboBox.SelectedIndex = 0;
});
What this does is that it creates a DependencyPropertyDescriptor object for the ComboBox.ItemsSource property, and then you can use that descriptor to register an event for any control of that type. In this case, every time the ItemsSource property of myComboBox is changed, the SelectedIndex property is set back to 0 (which means the first item in the list is selected.)

Related

How to detect when a templated UserControl is removed from UI (not just hidden in UI)

I have an ItemsControl with its ItemsSource bound to an ObservableCollection<T>, using my own UserControl as the ItemTemplate:
<ItemsControl ItemsSource="{Binding Path=MyObservableColletion, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<MyControls:MyUserControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I've got it hooked up so that the UI is updated as items are added/removed to/from the collection.
When an item is removed from the collection, I want to detect this in the code-behind of the representative templated MyUserControl. How can I do this?
I did notice that the Unloaded event fires in this case, but it's not adequate for my purpose because Unloaded also fires when the UI containing my ItemsControl is simply hidden/collapsed (e.g. when it's in a TabControl and the tab is switched away).
Is there another event for this purpose, or a way of detecting in the body of an Unloaded handler that my control is definitely removed and not just hidden?
since your your "MyObservableColletion" should be of type ObservableCollection you should be able to subscribe to it's "CollectionChanged" event. Its EventHandler will trigger on adding and removing. In the event args you will find The oldItems array that will contain the collection that was removed from the collection.
public void CollectionChangedEventHandler(object sender, NotifyCollectionChangedEventArgs args)
{
var deletedItem = args.OldItems.FirstOrDefault();
}
in WPF you do not deal with controls in code. never ever. You play with the data that is represented by controls.
We need to check if any property is changed for the dis-connected UserControl. One such property is DataContext, as after UserControl is not in the ItemsControl, so it's DataContext will be reset. And for dis-connected controls, it is set to {DisconnectedItem}.
Add this code in your UserControl.
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
if (e.Property.Name == "DataContext" && e.NewValue.ToString() == "{DisconnectedItem}")
{
System.Diagnostics.Debug.WriteLine(this + " : I am removed !");
}
base.OnPropertyChanged(e);
}

Programmatically set a data-bound combobox at window startup

There are other posts about this, but nothing that fits my situation on programatically setting the selected value.
I have a WPF window that has a combobox and as an input into that window, I have an ID value that corresponds to the values in this data-bound combobox. I've tried several ways to set the combobox.SelectedItem or combobox.SelectedIndex etc and each time, the value doesn't change. Looking deeper, since I'm performing this action from the window constructor after InitializeComponent() is called. Unfortunately, the combobox.Items is not populated, so the Items list has a count of 0. The binding is happening correctly, but obviously somehow it's not hooked up until after the window constructor is completed.
This is the trimmed down xaml for the combobox:
<ComboBox Name="Combobox_cmb" Grid.Column="0" Grid.Row="0" ItemsSource="{Binding Path=Names}" DisplayMemberPath="Name" SelectedValuePath="Name" SelectionChanged="Name_cmb_SelectionChanged" />
Of course "Names" is an observable list that gets loaded before trying to set the selected value. Even though the back end list is populated, if I look at the Combobox_cmb.Items after populating the list and before setting the selected value, Combobox_cmb.Items is empty.
Is there a way to pass in a value to the window and have a combobox default to that item?
Thanks
Have you tryed doing it in the Window.Loaded event? Try doing this:
public YourWindow()
{
InitializeComponent();
this.Loaded += Window_Loaded;
this.Datacontext = viewmodel // if you'r going with MVVM
}
public void Window_Loaded(object sender, RoutedEventArgs e)
{
Combobox_cmb.ItemsSource = ((Viewmodel)this.DataContext).Names; //Names should be in your viewmodel if you're going with MVVM. If not just use DataContext as this codebehind and place the list here.
}
Don't know if this helps cause I can't contextualize the answer. Maybe provide a bit more code.

Get binding object from Tap event

I'm trying to access the object that I bound a DataTemplate to, specifically I only need one int value. I've linked the main Grid from within the DataTemplate to an event handler via Tap:
<DataTemplate x:Name="joinTemplate">
<Grid Tag="{Binding index}" DataContext="{Binding}" Tap="select_Click" ...>
...
</Grid>
</DataTemplate>
My handler looks like:
private void select_Click(object sender, System.Windows.Input.GestureEventArgs e)
The problem is that I still can't access sender.DataContext or sender.Tag. However, when I run it in the debugger and look at it through Watch, I can get to both the DataContext and Tag by simply expanding "base" twice. That should mean that the object that I'm being given inherits those objects and is somehow the child of the original Grid, however, I thought that the sender was always the Grid you bound the handler to? To get the actual element that I tapped I'd have to use, for this example, e.OriginalSource, right?
Just cast sender to the appropriate type to access the DataContext property:
((FrameworkElement)sender).DataContext
Then, the same way, you'll have to bind the value to whichever type you binded to the grid. For instance, if you binded an object of type Model:
var model = (Model)((FrameworkElement)sender).DataContext

How to Sort a ListBox of Images by Date

I am binding a ListBox in my MainPage to a collection of images that have been taken using the CameraCaptureTask. Everything is working correctly, although I would like to be able to change the sort order from Ascending to Descending when respective RadioButtons in my SettingsPage are checked. I have created a value in IsolatedStorage that remembers which RadioButton is checked, so that when the MainPage of my application loads, the ListBox's binding collection will be sorted and displayed accordingly. The actual sorting of my collection, however, is where I am having issues. Note, which each image in the collection, a DateTaken property is also saved.
MainPage.xaml
<ListBox x:Name="Recent" ItemsSource="{Binding Pictures}" Margin="8"
SelectionChanged="recent_SelectionChanged"
</ListBox>
Now, in my Constructor I am setting my DataContext equal to PictureRepository.Instance, which is actually populated with the images from IsolatedStorage. I am not sure where or how to change the sort order of the collection before binding. I am thinking that in fact I would probably want to bind a copy of the sorted list, and in fact not change the sort order in IsolatedStorage. I was attempting to do something of the following as referenced from Sorting Listbox Items by DateTime values
MainPage.xaml.cs
public MainPage()
{
InitializeComponent();
DataContext = PictureRepository.Instance;
//Determine which Sort Radio Button has been Checked and display collection accordingly
//Also not sure if this should be performed in the OnNavigatedTo event
if (Settings.AscendingSort.Value)
{
//PictureRepository.Instance.Pictures.OrderBy(p => p.DateTaken).First();
//DataContext = PictureRepository.Instance;
var items = Recent.Items.Cast<CapturedPicture>().OrderBy(p => p.DateTaken).ToArray();
if (Recent.Items.Count != 0)
Recent.Items.Clear();
Recent.Items.Add(items);
}
else
{
//PictureRepository.Instance.Pictures.OrderByDescending(p => p.DateTaken).First();
//DataContext = PictureRepository.Instance;
var items = Recent.Items.Cast<CapturedPicture>().OrderByDescending(p => p.DateTaken).ToArray();
Recent.Items.Clear();
Recent.Items.Add(items);
}
}
Neither option has worked, although admittedly I have never tried sorting an ObservableCollection before populating a ListBox before. Any links, assistance, or advice would be greatly appreciated in learning this concept!
I prefer to use CollectionViewSource when sorting a ListBox. Instead of changing the backend collection that you are binding to, you allow the controls to handle this.
You page xaml:
<phone:PhoneApplicationPage.Resources>
<CollectionViewSource x:Key="PicturesViewSource" Source="{Binding Pictures}">
<!-- Add for design time help. This object should return a collection of pictures
<d:Source>
<viewModels:MyFakeObject/>
</d:Source>
-->
</CollectionViewSource>
</phone:PhoneApplicationPage.Resources>
<Grid>
<ListBox ItemsSource="{Binding Source={StaticResource PicturesViewSource}}"/>
</Grid>
In your page you can modify how the ColletionViewSource is sorted by adding or removing SortDescriptions. You would do this whenever the user changes the radio button.
PicturesViewSource.SortDescriptions.Clear();
PicturesViewSource.SortDescriptions.Add(new SortDescription("DateTaken", ListSortDirection.Descending));

Updating the DisplayMember of a ListBox

This question deals with a dinky little Winforms GUI. Let it be known that I have basic knowledge of data bindings and INotifyPropertyChanged and use both in my ViewModels in WPF. But I don't know Winforms. This is for a school assignment.
So I have a class that has a DisplayName property. I also have a ListBox whose Items are a sequence of instances of my class. I have pointed myListBox.DisplayMember = "DisplayName"; After changing a value in an instance of my class that will cause the DisplayName property to return a different value, how do I tell my ListBox to pull the DisplayName property again to refresh its value?
I needed to do the same thing but with a combobox. The workaround I found is to clear and reset the DisplayMember property.
This worked:
myComboBox.DisplayMember = null;
myComboBox.DisplayMember = "DisplayName";
It's important to note that this is perhaps not the best solution as it will cause multiple SelectedValueChanged events but the end result is successful.
Doing it this way probably requires re-binding the listbox, loosing selectedIndex etc.
One workaround is to forget about the DisplayMember property and handle the Format event of the ListBox instead. Something like (from memory) :
// untested
e.Value = (e.Item as MyClass).DisplayValue;
I know this was ages ago but I had similar problem and could not find satisfying solution and finally solved with this single line at the end after updating the values:
bindingsource.EndEdit();
Items on listbox reflects any changes entered into textboxes after Update button clicked. So after lines like this:
textbox1.DataBindings["Text"].WriteValue();
textbox2.DataBindings["Text"].WriteValue();
just insert this line:
bindingsourcevariable.EndEdit();
Hope this helps others who also encounter similar problem but haven't found the right solution
Here is solution code that does everything in XAML as opposed to back end C#. This is how I do my projects utilizing MVVM (minimizing the back end code, and if possible having no back end code)
<ListBox x:Name="lstServers" HorizontalAlignment="Left" Height="285" Margin="20,37,0,0" VerticalAlignment="Top" Width="215"
ItemsSource="{Binding Settings.Servers}"
SelectedItem="{Binding Settings.ManageSelectedServer, Mode=TwoWay}"
DisplayMemberPath="UserFriendlyName"/>
This is a listbox on the Window. The keys to point out here, which can be very tricky, are the usual ItemsSource property being set to a Settings object on my view model, which has a Servers Observable collection.
Servers is a class that has a property called UserFriendlyName.
public sealed class AutoSyncServer : ObservableModel
{
public AutoSyncServer()
{
Port = "80";
UserFriendlyName = "AutoSync Server";
Server = "localhost";
}
private string _userFriendlyName;
public string UserFriendlyName
{
get { return _userFriendlyName;}
set
{
_userFriendlyName = value;
OnPropertyChanged("UserFriendlyName");
}
}
This is a partial code snippet for you of the class itself.
The SelectedItem of the ListBox is bound to an instance of the Selected object that I store in the model view called ManageSelectedServer.
The tricky part here is the DisplayMemberPath is set to "UserFriendlyName" as opposed to "{Binding UserFriendlyName}". This is key
If you use {Binding UserFriendlyName} it will display the UserFriendlyNames in the collection but will not reflect any changes to that property.
The XAML for the TextBox where the user can update the user friendly name (which should change the text in the listbox also) is:
<TextBox x:Name="txtDisplayName" HorizontalAlignment="Left" Height="23" Margin="395,40,0,0" TextWrapping="Wrap"
Text="{Binding ElementName=lstServers,Path=SelectedItem.UserFriendlyName, UpdateSourceTrigger=PropertyChanged}"
VerticalAlignment="Top" Width="240"/>
This sets the Text property of the TextBox and binds it to the ListBox element lstServers SelectedItem property UserFriendlyName. I've also included an UpdateSourceTrigger=PropertyChanged so that any changes made to the text source notify that they have been changed.
XAML is tricky!

Categories