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.
Related
This question already has an answer here:
Filtering Multiple CollectionViews of the same ObservableCollection - WPF
(1 answer)
Closed 3 years ago.
Given a "customer" entity:
public class CustomerEntity: EntityBase
{
public Dictionary<int, string> ClientsOfCustomer = new Dictionary<int, string>();
public CustomerEntity()
{
// Load ClientsOfCustomer...
}
}
and two or more WPF ComboBoxes that have their ItemsSourceProperty bound to the same source (say, an attribute of the above "Customer" entity):
var comboBox1 = new ComboBox();
var comboBox2 = new ComboBox();
comboBox1.SetBinding(ItemsControl.ItemsSourceProperty,
new Binding(itemsSourceProperty) { ElementName = "ClientsOfCustomer" });
comboBox2.SetBinding(ItemsControl.ItemsSourceProperty,
new Binding(itemsSourceProperty) { ElementName = "ClientsOfCustomer" });
The above is done before we actually have an instance of the underlying object. This happens sometime later in the application:
var customer = new CustomerEntity();
parentPageOfComboboxes.DataContext = customer;
if we then filter the ItemCollection of either ComboBox:
comboBox1.Items.Filter = i => ((KeyValuePair<int, string>)i).Value.StartsWith("a");
this ends up filtering the other ComboBox as well.
I understand that this is because ComboBox is a Selector, which is an ItemsControl, and ItemsControl invokes ItemCollection.SetItemsSource when the ItemsSource DependencyProperty changes. ItemCollection then in turn makes use of CollectionViewSource.GetDefaultCollectionView( _itemsSource, ModelParent, GetSourceItem) to grab the default CollectionView associated with the given _itemsSource from cache. Therefore if you set the Filter on the underlying ItemCollection of one ComboBox, you are actually setting it on the cached and shared CollectionView associated with the ItemsSource that the ComboBox is bound to. I guess the WPF folks didn't expect anyone to want to do so - unfortunate. But what can I do about it?
I've found two similar questions on SO without any real answer: "Filtered Combobox ItemsSource binding issue" (which seems to be about a custom "FilteredComboBox") and "Wpf ListBoxes' ItemsSource strange behaviour (which involves listboxes without data binding).
I've also implemented a fairly ugly solution that I will include below as an answer. However, there must be a better way.
UPDATE/CLARIFICATION: I did not express myself clearly in my original question. The answer to this question is trivial in cases where we have access to the actual instance of the entity that we are binding to at the time of data binding. However, I have a complex application that generates forms "on-the-fly" from metadata (which can come from C# Attributes, or from meta-data stored in a database, it doesn't matter). In the other similar questions posed on SO, the actual binding source (the data context) is always available, so simply doing something like Items = new ListCollectionView(...); (either in code or in XAML) is possible. But what if the actual DataContext is only set somewhere much further down the application lifecycle based on metadata? What solution is there other than something very crude like looping through all generated UI elements and altering the binding source retroactively? I hope this clarification is understandable.
An Ugly Solution
I created my own MyComboBox that inherits from System.Windows.Controls.ComboBox. In MyComboBox I override metadata for the ItemsSourceProperty as described in the docs so that whenever the ItemsSource changes I actually bind to a new ListCollectionView:
public partial class MyComboBox : ComboBox
{
static MyComboBox()
{
ItemsSourceProperty.OverrideMetadata(typeof(MyComboBox),
new FrameworkPropertyMetadata((IEnumerable)null, new PropertyChangedCallback(OnItemsSourceChanged)));
}
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ItemsControl ic = (ItemsControl)d;
IEnumerable oldValue = (IEnumerable)e.OldValue;
IEnumerable newValue = (IEnumerable)e.NewValue;
// We get into infinite recursion land without the following condition:
if (newValue.GetType() == typeof(Dictionary<int, string>))
{
var cvs = new CollectionViewSource() { Source = ((Dictionary<int, string>)newValue) };
ic.ItemsSource = cvs.View;
}
}
...
}
Note that the if (newValue.GetType() == typeof(Dictionary<int, string>)) condition is required, otherwise you get into an infinite recursion loop (MyComboBox sets ItemsControl.ItemsSource which changes the ItemsSourceProperty, which then triggers ic.OnItemsSourceChanged(oldValue, newValue) leading back to MyComboBox.OnItemsSourceChanged and so on.
Another caveat: simply doing ic.ItemsSource = new ListCollectionView(((Dictionary<int, string>)newValue).ToList()); would seem to work except that any changes to the underlying ObservableCollection will not update the bound ComboBox. So it seems necessary to create a new CollectionViewSource, set the Source and bind to the view as above. This is also what is recommended in the official docs for CollectionView:
You should not create objects of this class in your code. To create a
collection view for a collection that only implements IEnumerable,
create a CollectionViewSource object, add your collection to the
Source property, and get the collection view from the View property.
So the above works but it's pretty ugly. Is there really no better way?
Your observations are correct. Every ItemsControl that binds to the same source collection, shares a common default ICollectionView of this collection.
The solution is to create dedicated instances of ICollectionView for each ComboBox. You can define them in XAML using a CollectionViewSource like in the example below. If the filter is triggered by aButton you can use a command to bind this Button to filter predicate defined in your view model, passing the CollectionViewSource as CommandParameter.
Otherwise define the ICollectionView as properties in your view model and bind to them. You can now set the filter predicate directly without using a trigger (command) from the view.
ViewModel.cs
public ObservableCollection<string> Data source { get; set; }
ICommand ApplyFilterCommand => new RelayCommand(FilterCollectionView);
private void FilterCollectionView(object param)
{
CollectionView collectionView = (param as CollectionViewSource).View;
collectionView.Filter = item => item.StartsWith("a");
collectionView.Refresh();
}
MainWindow.xaml
<Window>
<Window.DataContext>
<ViewModel />
<Window.DataContext>
<Window.Resources>
<CollectionViewSource x:Key="FirstCollectionSource"
Source="{Binding DataSource}" />
<CollectionViewSource x:Key="SecondCollectionSource"
Source="{Binding DataSource}" />
<Window.Resources>
<StackPanel>
<ComboBox ItemsSource="{Binding Source={StaticResource FirstCollectionSource}}" />
<ComboBox ItemsSource="{Binding Source={StaticResource SecondCollectionSource}}" />
<!-- Filter the second ComboBox -->
<Button Command="{Binding ApplyFilterCommand}"
CommandParameter="{StaticResource SecondCollectionSource}" />
</StackPanel>
<Window>
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.
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;
To date every ListView I've had I just set ItemSource={Binding} in my Xaml and then in the .CS file I say listview.datacontext = myobject and the view loads just fine. But now I need to have a list that updates as the data updates as well. So after some research I discovered ObservableCollections and rewrote my code to use that. But I can't get my data to display when setting the listview to my dataobject.
My Xaml:
<ListView ItemsSource="{Binding Tests}" Name="DataCompareTests" Margin="0,0,5,0" Grid.Column="0">
<ListView.View>
<GridView>
<GridViewColumn Header="TestCase" Width="200" DisplayMemberBinding="{Binding name}" />
</GridView>
</ListView.View>
</ListView>
My Xaml.cs:
readonly DataCompare dataCompare = new DataCompare();
public void Execute_Click(object sender, RoutedEventArgs e)
{
var Tests = new ObservableCollection<TestCases>();
Tests = dataCompare.LoadTestCases(); //located in another class file
//DataCompareTests.DataContext = Tests;
}
If I remove the "Tests" part of the binding in my Xaml and remove the comments from the .DataContext line above, the view displays the correct information. However it's my assumption that if I want my view to update as the data does I need to specify my object in the binding. How do I properly set that? I can't seem to find the correct answer.
Thanks,
Jason
I think you need to familiarize yourself a little better with bindings and object oriented programming in general.
If you set your datacontext to your model object, ".Tests" should be a public property of that model object. Also, don't do this:
var someVariable = new SomeClassThatTakesWorkToConstruct();
someVarialbe = someOtherVariable.SomeMethod();
What you meant to do was this:
var someVariable = someOtherVariable.SomeMethod();
This is for 2 good reasons 1) You are not wasting the construction of an ObservableCollection. 2) Your code will be easier to refactor (the type returned by SomeMethod can change without you having to alter your declaration of someVariable).
Edit, additional resources:
Databinding Overview
You've got a path specified but no source for the binding specified.
MVVM Article
Great article on using the common MVVM WPF pattern, helps you keep your code object oriented, clean, etc. even with complex UI interaction.
It would appear my concerns were pointless and I WAS doing this the proper way in the first place.
According to MSDN:
"However, if you are binding to an object that has already been created, you need to set > the DataContext in code, as in the following example.
...
myListBox.DataContext = myDataSet;"
My object was already created, and I did set the DataContext in the code. All I had to do was leave the ListView ItemSource as {Binding} and each time I added to the Tests object, the list updated..
I can't believe I spent an entire day doubting I was doing this correctly without moving forward to check. :-)
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!