Programmatically set a data-bound combobox at window startup - c#

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.

Related

Binding data to a data-grid in WPF with Entity Framework

I've looked all over the web for this, and nothing I found seems to help.
I made a model and added the model to a data source as an object. I assumed it would work like a data set where I can just drag and drop onto a form and it would bind the data for me. But it keeps showing blank when I drag and drop from the model. so I looked online and saw that some code-behind was required and this is what I have and its still blank. Any ideas what Im doing wrong?
public partial class form1: Window
{
ComEntities context;;
public form1()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
context = new ComEntities();
System.Windows.Data.CollectionViewSource comEntitiesViewSource = ((System.Windows.Data.CollectionViewSource)(this.FindResource("comEntitiesViewSource")));
var permits = (from c in context.tBLPER.Local select c);
this.DataContext = context.tBLPER.Local;
tBLPERDataGrid.ItemsSource = context.tBLPER.Local;
}
}
XAML:
<DataGrid x:Name="tBLPERDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected" Margin="10,10,10,413" ItemsSource="{Binding}" EnableRowVirtualization="True" AutoGenerateColumns="False">
</DataGrid>
You need to materialize your query (bring the data to memory). You can do that calling the ToList() method, but even better is do this:
context.TBLPER.Load();
this.DataContext = context.TBLPER.Local; // set the Window DataContext property
Local property gets an ObservableCollection<T> that represents a local view of all Added, Unchanged, and Modified entities in this set. This local view will stay in sync as entities are added or removed from the context. Likewise, entities added to or removed from the local view will automatically be added to or removed from the context.
In case you need to filter your data before (suppose your entity has a property named Age and want the users older than 20), then you can do this:
context.TBLPER.Where(t=>t.Age>20).Load();
this.DataContext = context.TBLPER.Local;
Another thing, if you want to set the ItemSource property of your Grid in the code behind of your window, it don't make sense create a binding to that property in your xaml code, so remove it:
<DataGrid ... ItemsSource="{Binding}" ...>
If you are going to do this:
tBLPERDataGrid.ItemsSource=context.TBLPER.Local;
You shouldn't be setting ItemsSource twice (just set it in your code behind - remove ItemsSource="{Binding}").
Also, you should set AutoGenerateColumns="True" because without that you need to add DataGridXColumn elements to the DataGrid.
Have a look here for more details ... http://www.wpf-tutorial.com/datagrid-control/custom-columns/
You may also want to put a breakpoint on the tBLPERDataGrid.ItemsSource = permits; line so you can inspect permits to confirm it contains the data you expect.
If you set AutoGenerateColumns="False" you should provide the columns definition on XAML or set AutoGenerateColumns="True". This is the first thing to do.
If You want to use Model (also ItemsSource="{Binding}" is a hint for me, that You want), than don't hard code the ItemsSource. You create the Model's object in a CEntities context; variable, but You should set it to the DataContext property of the Window like this:
DataContext = new CEntities();
and remove the line
tBLPERDataGrid.ItemsSource = permits;
Now the ItemsSource is coming from the CEntities instance.

WPF Combobox display value doesn't update

I am having a furious struggle with the WPF Combobox in our bi-languge application.
I have the combobox binded to a collection. I have a button that replaces the values of the collection with their corresponding string values in another language.
What is the problem: I select a value from the drop down list. I change the language, by pressing a button, then the displayed value remains on the old language, but when the drop down is dropped the values in in are replaced with the right ones.
Here is my XAML:
<ComboBox x:Name="ProjectClassComboBox"
Width="150"
ItemsSource="{Binding Path=ProjectClassCollection}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding Path=RegionContext.CurrentItem.ClassNomenclatureId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Update: Because it is asked in the comments I must add that
the collectionis a custom class of ours, DERIVED from ObservableCollection, that is fairly complex. The collection items must be implementing INotifyPropertyChanged, and the collection has a listener to the PropertyChanged event of each item.
It just looks like the displayed text of the combo is not updated, when the drop down list and the selected item value is updated.
Binding ObservableCollection (and derrived class too) works only in case where you add or delete items, cause that's the action that invokes change event. If you need to manipulate data inside collection I suggest using BindingList. Maybe some kind of wrapper would be solution for you.
Do one thing. In the button click,
1. get the selected index in the combo box
2. Replace all the strings in the collection
3. set the selecteditem property of the combobox using the selected index that we have stored earlier in the step 1.
So, it's a bit late but we just encountered the same problem on a project that needs to support multiple languages.
Our solution was to return a new instance of ObservableCollection on the property getter.
So, your code should look like this:
<ComboBox x:Name="ProjectClassComboBox"
ItemsSource="{Binding Path=ProjectClassCollection}"/>
And in your ViewModel:
public ObservableCollection<Project> ProjectClassCollection
{
get {return new ObservableCollection<Project>(){_projectClassCollection};}
set {...}
}
This code is a quick snippet from my memory. It will not work if you just copy-pasta, but the idea is that another collection instance worked for us.
We tried to call OnPropertyChanged(nameof(ProjectClassCollection)) but that didn't work. We tried to set UpdateSourceTrigger=PropertyChanged in XAML but that didn't work either. Having a new instance worked.
Hope this helps and saves you time, cheers!

wpf excel like grid editing?

In a project I have a very tricky requirement I don't know how to solve:
I have several datagrids in a single wpf window (I use MVVM) all binded to some collection in the linked ViewModel.
The customer wants to edit each of these grids, either within the grid or in a common textbox (like in excel).
I'm banging the head on how to do the latter. What I would do is bind the textbox with a property in the viewmodel, but when the value is changed there, I need to change the value in the original property binded with the datagrid cell accordingly. In other words, I need to know what collection and which property of that collection I need to change with the data in the textbox accordingly .
I tried several ways but with no luck.
Reflection? DependencyProperty? What else?
Any help?
Thank you
Assuming that you're using the built-in WPF DataGrid, you'll need to setup your grid similarly:
<DataGrid SelectionUnit="Cell" SelectionMode="Single" ItemsSource="{Binding Data}" SelectedCellsChanged="DataGrid_OnSelectedCellsChanged">
...
</DataGrid>
Also give your TextBox a name:
<TextBox x:Name="textBox" DockPanel.Dock="Top" />
In the code-behind, you'll need to manually wire up this event, since apparently the DataGrid doesn't allow you to bind to the selected item/cell/value when using SelectionUnit="Cell":
private void DataGrid_OnSelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
{
if (e.AddedCells.Count == 0)
this.textBox.SetBinding(TextBox.TextProperty, (string) null);
else
{
var selectedCell = e.AddedCells.First();
// Assumes your header is the same name as the field it's bound to
var binding = new Binding(selectedCell.Column.Header.ToString())
{
Mode = BindingMode.TwoWay,
Source = selectedCell.Item,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
};
this.textBox.SetBinding(TextBox.TextProperty, binding);
}
}
I tried getting this done without code-behind but after looking around it didn't seem like this was possible.
In addition to tencntraze answer I used this code to get te property bound to the cell
var property = (selectedCell.Column.ClipboardContentBinding as Binding).Path.Path;

ComboBox.SourceUpdated event is not fired

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.)

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