What should Binding Path property be set to? - wpf

Assuming I have this struct definition in C#:
public struct TimeSlotInfo
{
public int TimeSlotID;
public int StartMin;
public int CalcGridColumn;
public string BackgroundCol;
public bool ToDisable;
}
And I have a linq query as so:
var TimeSlotsInfo =
from ts in datacon.TimeSlots
select new TimeSlotInfo
{
TimeSlotID = ts.TimeSlotID,
StartMin = ts.StartMin,
CalcGridColumn = CalcTimeSlotGridColumn(ts.StartMin),
BackgroundCol = ts.ColorName,
ToDisable = false
};
If i set the ItemsSource property of say a ListBox as below:
lstBox.ItemsSource = TimeSlotsInfo;
Now, how do i set a binding path to reference the "BackgroundCol" field from the above query result?
I've tried {Binding Path=TimeSlotInfo.BackgroundCol}, {Binding Path=TimeSlotInfo/BackgroundCol}, and finally {Binding Path=BackgroundCol}...none of them seem to be working..
Can anyone help? I've tried to simplify the example as possible. Hope my problem is clear enough.
Thanks in advance.

The last one is correct ({Binding Path=BackgroundCol}) - however, you can't bind to fields, you can only bind to Properties. Define your class to be:
class TimeslotInfo {
public int TimeslotId {get; set;}
/* Etc... */
}

Not only should you use properties for binding as Paul says, but in general you should avoid public fields in the first place.
In addition, this doesn't feel like it should be a struct - do you really want value-type semantics? When in doubt, you should default to creating classes in C# - it's pretty rare that you really want a struct.
Finally, even if you did want a struct, you should almost always make structs immutable. You're almost certain to have unexpected results from mutable structs. It's all well defined and for good reasons, but it's probably not the behaviour you expect. Mutable structs are evil.

If you want your items to display whatever is stored in BackgroundCol, you can just set the DisplayMemberPath property on your ListBox to "BackgroundCol". If this isn't what you're trying to achieve, please be more specific.
Hope this helps!!

Related

why this wpf data binding fails

I'm a newer to study Wpf Data Binding feature, and recently I met one problem puzzled me a lot.
Assume we have some code like this in ViewModel:
private Person person;
public Person Person
{
get { return person; }
set
{
SetProperty<Person>(ref person, ref value, ()=>Person);
}
}
Ignore the terrible naming. Then I changed the Person property as following in Command's Execute() method:
//try 1
private void UpdateInfoExecute()
{
Person.Name="somebody";
Person.Age=22;
}
Finally, the data binding failed. As I change the implementation like this:
//try 2
Person = new Models.Person() { Age=22,Name="somebody"};
It succeed!
Well, in my opinion, try 1 also changed Person's property just like try 2 do. The PropertyChanged event listen on Person property, and sure it would be changed after try 1 did.
I don't know the theory of this, can anyone point me the key I missed?
I don't know what exactly SetProperty does, but in your "try1" you are changing Name and Age, not Person. So Name and Age properties are raised, the reference of Person was not changed and stayed the same. In your "try2" you actually set a new Person, therefore the property was changed and raised.
Solution: Make sure that Person also implements INotifyPropertyChanged and make sure that each property raises a PropertyChanged. Then everything should work fine.
Remember .Person is like .Name just a property.

A method set as ICollectionView.Filter sees other properties in the class as null even though they are not null

I'm trying to implement a basic filtered list box in WPF. The user types something and the list is narrowed to the values beginning with the typed phrase.
I have:
a View with:
a TextBox whose Text property is bound to InstitutionFilteringString property in the ViewModel class, which is set as the data context,
a ListBox whose ItemSource property is bound to an ICollectionView named Institutions in the View Model
a ViewModel class with the properties mentioned above.
Code (with irrelevant parts cut out):
class ChooseInstitiutionAndPublisherPageViewModel : WizardPageViewModelBase
{
private ICollectionView _institutions;
public ICollectionView Institutions
{
get
{
return _institutions;
}
set
{
_institutions = value;
NotifyPropertyChanged("Institutions");
}
}
private string _institutionFilteringString;
public string InstitutionFilteringString
{
get
{
return _institutionFilteringString;
}
set
{
_institutionFilteringString = value;
NotifyPropertyChanged("InstitutionFilteringString");
//WORKAROUND
//Institutions.Filter = new Predicate<object>(FilterInstitutions);
Institutions.Refresh();
}
}
public ChooseInstitiutionAndPublisherPageViewModel(WizardViewModel parent)
: base(parent)
{
Institutions = CollectionViewSource.GetDefaultView(CentralRepository.Instance.GetInstitutions());
Institutions.Filter = new Predicate<object>(FilterInstitutions);
}
private bool FilterInstitutions(object obj)
{
//I may refer directly to the field or through the property, it doesn't change anything
if (_institutionFilteringString == null || _institutionFilteringString.Length == 0)
return true;
//some more filtering, irrelevant
//[cut]
}
}
The view and the binding:
<TextBox Text="{Binding Path=InstitutionFilteringString, Mode=TwoWay}" Height="23" Margin="6,6,87,0" Name="institutionNameTextBox" VerticalAlignment="Top" TextChanged="institutionNameTextBox_TextChanged" />
<ListBox Margin="6,35" Name="institutionsListBox" ItemsSource="{Binding Path=Institutions}" IsSynchronizedWithCurrentItem="True" />
So, to the point. The setter for the InstitutionFilteringString is called correctly. Following an advice from here, the setter calls a Refresh() method on the collection view. The FilterInstitutions() method is called.
And now the bug: even though the string was set just before a second, inside the FilterInstitutions method it's null. If I go with the debugger down the call stack, from the point of view of the setter it's still set to the typed value, but inside the filtering method it's null.
In the setter there is a commented-out line of code. Uncommenting it fixes the bug, but it's hardly how it should be done.
What am I doing wrong?
(I'm not sure, but it seems to me as if the setter and the filtering method operated on two different instances of the class. But how is it possible, I create just one instance and the class is not clonable)
EDIT
I'm sorry, it seems I've lied. I've put a breakpoint in the constructor and it seems I indeed create two instances of the class and CollectionViewSource.GetDefaultView returns the same instance of ICollectionView for both. Well, but I want actually to have two views for the same collection. Well, I've followed this answer and it seems to work :)
do you create your Institutions once? and set the
Institutions.Filter = new Predicate<object>(FilterInstitutions)
once? if yes its ok :) can you post your code for this and also the code for FilterInstitutions methode? i do it all the way in my projects and have no problems.

WPF - Reload an a collection (with 'new') without rebinding controls

Imagine the following:
class Repository
{
private ObservableCollection<ModelClass> _allEntries;
public ObservableCollection<ModelClass> AllEntries
{
get { return _allEntries; }
set { _allEntries = value; }
}
public void RefreshDataFromDB()
{
_all = new ObservableCollection(GetMyData()); // whatever method there is
}
}
Now there are a couple of controls that bind to this collection, e.g.:
<ListView ItemsSource="{Binding Repository.AllEntries, ElementName=Whatever}"/>
The problem now is that if I call the RefreshDataFromDB the bindings get lost (at least it seems so) as the _all is now pointing to new memory part and the bindings still use the old reference. INotifyPropertyChanged does not help me in this case (e.g. putting it in RefreshDataFromDB does not help a lot).
The question would be - how would you handle a case where you replce a collection and want to update its consumers' bindings?
Yes; you're not modifying the collection, the UI is bound to the collection, and then you replace it with a new one.
You could do this:
_all.Clear();
_all.AddRange(GetMyData());
Hope that helps!
Alternatively, make AllEntries (or All.. your nomenclature seems to change a few times on the post ;)) a DependencyProperty:
public static DependencyProperty AllEntriesProperty = DependencyProperty.Register("AllEntries", typeof(ObservableCollection), typeof(MyClass));
You'd need to make the get/set property too, see here for an example:
http://msdn.microsoft.com/en-us/library/ms752914.aspx

How to filter Observable Collection Class Collection

I have implemented Linq-To-Sql..
Add necessary table in it...
after that linq class will automatically set property for field..
I implemented one class using ObservableCollection class.. and pass datacontextclass object in its constructor...
so after getting all data how to filter it?
public class BindBookIssueDetails : ObservableCollection
{
public BindBookIssueDetails(DataClasses1DataContext dataDC)
{
foreach (Resource_Allocation_View res in dataDC.Resource_Allocation_Views)
{
this.Add(res);
}
}
}
private BindBookIssueDetails bResource;
bResource = new BindBookIssueDetails(db);
_cmbResource.ItemSource=bResource;
Please Help me.
You can use CollectionViewSource and filter it. So that it affect only at the View(.XAML) side
ICollectionView collectionView = CollectionViewSource.GetDefaultView(bResource);
collectionView.Filter = new Predicate<object>(YourFilterFunction);
Check out this blog for more details. http://bea.stollnitz.com/blog/?p=31
I tried to use #Jobi's solution but for some reason I got an exception trying to fire FilterFunction.
So I used a slightly different approach. I cast CollectionViewSource's DefaultView to a BindingListCollectionView
myVS=(BindingListCollectionView)CollectionViewSource.GetDefaultView(sourceofdata);
and now I can construct an SQL-like filter string and apply it like that:
myVS.CustomFilter=myfilterstring;
I will still try to resolve my problem (I presume #Jobi's solution is more flexible).

WPF Binding: Refreshing Binding after reload of combos from database

I've got two combo's 'Make' and 'Model', they've got their SelectedValue properties bound to an Vehicle object with a ModelID and a MakeID.
Heres Model ...
<ComboBox DisplayMemberPath="Description" ItemsSource="{Binding Path=ModelSpecs}" SelectedValue="{Binding Path=Vehicle.ModelID}" SelectedValuePath="ID" />
A user can search for Vehicles in a seperate control and this swaps out the underlying Vehicle object. Everything works fine if your switching between vehicles of the same Make, however if the Make changes I go away to the database and reload the ModelSpec collection. The combo dosnt display the Model Description because the binding needs to be refreshed.
My current work-around is to add this at the end of the method thats reloading the Models - it works fine, but is not a particularly elegent solution.
var modelID = ViewModel.Vehicle.ModelID;
ViewModel.Vehicle.ModelID = string.Empty;
ViewModel.Vehicle.ModelID = modelID;
Basically I'm just triggering the INotifyPropertyChanged ...
private string _modelID;
public string ModelID
{
get { return _modelID; }
set
{
if (_modelID == value) return;
_modelID = value;
OnPropertyChanged("ModelID");
}
}
I can think of a couple of similar inelegant solutions - but there must be a better way?! Any help appreciated!
Just make ModelSpec collection observable (i.e. implement INotifyCollectionChanged yourself, or use ObservableCollection class for it).
Well, this is probably just another "inelegant" solution, but one more correct way would be to get the BindingExpression from the combo-box and call BindingExpression.UpdateSource.
Thanks for you assistance, in the end this did the trick and I prefer it to my first workaround.
It seems fine to me, but I guess others may gasp in horror? Please feel free to comment if so!
ModelSpecs is on my ManageVehicleViewModel so it dosnt seem that out of place to have the extra PropertyChanged call.
private IEnumerable<ModelSpec> _modelSpecs;
public IEnumerable<ModelSpec> ModelSpecs
{
get
{
return _modelSpecs;
}
set
{
if (_modelSpecs == value) return;
_modelSpecs = value;
OnPropertyChanged("ModelSpecs");
OnPropertyChanged("Vehicle");
}
}

Resources