Hi all
I have a combobox which is databound to a list of elements. But in addition to that list of elements, i want to have another item. This item should display the text 'New...'
The idea is if they select one of the normal elements, it performs some action involving that element. If they select the 'New' element, it will take them to a screen where they can create a new item.
The problem is, When you databind something you dont get the option to add another item to it, and there is no question of adding a dummy item to the list of elements...
Is this an opportunity to create a new control based on the ComboBox which has a 'DefaultElement' property? (with all of the associated templating and command binding etc)
To do this I have previously created a dummy wrapper class for the normal type, allowing you to bind to a list containing mostly the correct values and also your "New..." one, e.g.
public class DisplayClass
{
public DisplayClass(ModelClass mc)
{
this.mc = mc;
}
public string Name
{
get { return this.mc != null ? this.mc.Name : "New..."; }
}
public bool IsDummy
{
return this.mc == null;
}
public ModelClass Model
{
return this.mc;
}
}
You can then host a collection of these in your data context (ViewModel), and handle the selection appropriately based on IsDummy. It's not as automatic as a control with this functionality built in, but is pretty simple and could probably easily be made generic and so reusable.
Set the ItemsSource property to a CompositeCollection with the new item and bound collection together, then detect that item's selection based on selected index or something similar.
Example Code:
<ComboBox>
<ComboBox.ItemsSource>
<CompositeCollection>
<ComboBoxItem>Add New Item...</ComboBoxItem>
<CollectionContainer Collection="{Binding Source={StaticResource CollectionSource}}"/>
</CompositeCollection>
</ComboBox.ItemsSource>
</ComboBox>
MSDN for CompositeCollection: http://msdn.microsoft.com/en-us/library/system.windows.data.compositecollection(v=vs.110).aspx
Keep in mind that what you bind to is a UI oriented collection of items which can be different than the business or data entities.
If I were you, I'd insert a 'new' entity in the first position of the bound collection and detect it in my viewmodel to trigger the appropriate action when the user selects it.
Related
I have a combo box in a datagrid located in an activity. Based on combobox selection I populate another grid with controls programatically. User enters some data in these controls and then saves it. The object that the combo box is bound has many properties of which two are used in selected value path and display member path. The data is bound using two way binding for combo box. When the saved activity that has been placed on a workflow is reopened the data is reloaded correctly and the correct object is value is set in the combo box. But on UI rendering only the values that are attached with the combo box remain intact (i.e those in selected value path and display member path) the rest are reset.
Any idea why this might be happening?
P.S: Setting the binding to OneTime solves the problem of retrieval but any changes that are made on the UI after loading are not reflected back.
Code-Behind:
public ObservableCollection<MyRule> AllRules {get;set;}
public MyRule myRule{get;set;}
In datagrid Loaded Event I populate the AllRules as:
AllBusinessRules.Add(new MyRule () { RuleId = item.Id, RuleName = item.Name});
where item.Id and item.Name are obtained from the database via service call.
In the same event if I also load any previously saved rules as:
myRule=SelectedRule;
where SelectedRule has RuleId, RuleName, Inputs and Outputs as well.
Code:
<ComboBox
ItemsSource="{Binding Path=AllRules}"
SelectedItem="{Binding Path=myRule,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"
SelectedValuePath="RuleId"
DisplayMemberPath="RuleName">
<DataTemplate>
<TextBox Text="{Binding Path=myRule.RuleName}"/>
</DataTemplate>
</ComboBox>
Class:
public class MyRule{
public int RuleId{get;set;}
public string RuleName{get;set;}
public List<string> Inputs{get;set;} //properties that are reset when the UI renders
public List<string> Outputs{get;set;} //properties that are reset when the UI renders
}
The Inputs and Outputs properties are obtained from the programatically generated controls via reflection and added to the object populated by combobox and saved.
I have studied about this problem here but the solution does not solve my problem. Any help would be great.
SelectedValuePath, DisplayMemberPath are set wrongly. DisplayMemberPath should be "RuleName". SelectedValue, SelectedValuePath are not needed as you have set SelectedItem. SelectedItem will get the chosen item automatically because of Binding. From the myRule object you can access other properties.
It took a lot of time to investigate what was wrong but now that I know its just quite simple.
As I have shown that in the datagrid's Loaded event I used to set the ItemsSource of the combo box and in the item source I have only set the properties RuleId and RuleName.
Problem:
So the problem was that when I assigned the value i.e the selected value on reloading the combobox e.g. myRule=SelectedRule the other properties i.e. Inputs and Outputs are not there in the ItemsSource. That is why the selected object though correct did not have Inputs and Outputs as the SelectedItem was from ItemsSource of the Combo Box giving me the impression that the two way binding had somehow reset the values of properties not bound with the combo box.
Solution:
In the end I wrapped my MyRule Object in another object like RuleInformation i.e
public class RuleInformation{
public List<string> Inputs;
public List<string> Outputs;
public MyRule myRule{get;set;}
}
where MyRule is like:
public class MyRule{
public int RuleId{get;set;}
public string RuleName{get;set;}
}
So the combo box is bound to the MyRule object whereas the inputs and output properties remain untouched in the upper object.
I am trying to bind my collection to a listbox. The collection contains some items which are to be hidden and are to be shown based on certain conditions.But when I do this the alternate styles are not applied properly.
For example:
Case 1 : where all items are visible, I get the output like this:
Item1(grey)
Item2(white)
Item3(grey)
Item4(white)
Item5(grey)
Item6(white)
Item7(grey)
Case2: where Item2 is hidden, I get the output as:
Item1(grey)
Item3(grey)
Item4(white)
Item5(grey)
Item6(white)
Item7(grey)
How do I resolve this without rebinding the collection?
Rather than hide the items (presumably in the ListBoxItem control template, or the ListBox ItemTemplate), you should use a CollectionViewSource to filter them out.
This is because the items are still technically there - you cannot see them but the listbox can.
See this link for details on filtering.
http://wpftutorial.net/DataViews.html
To filter a collection view you can define a callback method that
determines if the item should be part of the view or not. That method
should have the following signature: bool Filter(object item). Now set
the delegate of that method to the Filter property of the
CollectionView and you're done.
ICollectionView _customerView = CollectionViewSource.GetDefaultView(customers);
_customerView.Filter = CustomerFilter
private bool CustomerFilter(object item)
{
Customer customer = item as Customer;
return customer.Name.Contains( _filterString );
}
I have a listview which binds customer informations from database. There are 15 columns which are binded in that listview. One of that column is Customer Name.
I want to be focused them when I type their name's initial character from the keyboard. Have you any idea to make this ?
This is my listview's XAML code
<ListView x:Name="datalist" ButtonBase.Click="datalist_Click" ContextMenuOpening="datalist_ContextMenuOpening" MouseDoubleClick="datalist_MouseDoubleClick" SelectionChanged="datalist_SelectionChanged"
MouseUp="datalist_MouseUp" PreviewMouseUp="datalist_PreviewMouseUp" >
Try to operate only on Model/ModelView and not on UI directly (as much as it possible).
For example, define in ModelView a property
public bool Focused{
get ..
set... //OnPropertyChanged
}
nd bind it to the UI elements corresponding property.
After this the only thing you need to do is t simply
find an element in binded data (ModelView objects)
set it's Focused property to true
I have such DataGrid
<DataGrid AutoGenerateColumns="True" HorizontalAlignment="Stretch" Name="dataGrid1" VerticalAlignment="Stretch" ItemsSource="{Binding DataList}" IsReadOnly="True"/>
In my ViewModel I have such field:
public ObservableCollection<ConsoleData> DataList { get; set; }
And such method which is called every second:
private void model_DataArrived(List<ConsoleData> dataList)
{
DataList.Clear();
dataList.ForEach(x => DataList.Add(x));
}
Grid displays some real-time data and updates every second.
The problem is - when I select some row in the grid, the selection is reset after a second (when new data is arrived).
I guess probably this is because I Clear DataList every time?
How to solve this problem?
Before clear, pick up the currently selected item (a unique identifier if you have one) then attempt to highlight it again on update and if it's not there anymore just don't highlight annything.
The way I've set up a updating lists in the past is:
Create an Update method in you object (ConsoleData) that you can pass a copy of that object and the object updates itself. The object also needs to implement INotifyPropertyChanged.
In you model_DataArrived method in the ViewModel find all matching objects and use the Update method from step 1 to update the objects.
Find all new objects and add them to you list (DataList).
Find all missing objects and remove them from you list (DataList).
In case the new dataSource still contains your last selected item and if you are following MVVM pattern. All you need to do is Raise PropertyChanged event for your selecetdItem once data source is reloaded. Make sure your viemModel implements INotifyPropertyChanged interface.
EDIT
And in case you don't want to clear your datasource every now and then. Simply, use the ObservableCollection in place of the generic list. It internally implements INotifyCollectionChanged, so any addition or deletion of item in your collection will be reflected on your UI.
I have a combo box that is bound to a collection that is essentially a list of Name/Value pairs. The collection can have multiple items with different names, but the values may be the same.
public class NameValuePair
{
public string Name { get; set; }
public string Value { get; set; }
}
public class NameValuePairCollection : List<NameValuePair>
{
public NameValuePairCollection(): base() { }
}
So inside my User Control I have a private field called items which is an instance of that NameValuePair collection:
private NameValuePairCollection items = new NameValuePairCollection()
Somewhere along the lines that collection gets initialized and items get added to it. However, the problem I see is when I try to set the selected index of the combo box that is bound to this collection:
this.CboItemsSelector.SelectedIndex = 3;
or
this.CboItemsSelector.SelectedItem = this.items[3];
The selected item is there but the UI is not synchronized. The UI's selector still defaults to the first item in the list, even thought the SelectedItem's Name and Value properties DO IN FACT CORRESPOND to whatever is in index 3 of the underlying collection!
Any ideas on how to force the ComboBox to refresh itself? Or just plain fix the issue? I know it's quite small issue, but it is big enough to force me to rewrite quite a bit of code.... :( :( :(
Thanks!
You need to inherit from ObservableCollection, not List. Otherwise no OnPropertyChanged events will be fired and the bound control wont know the data has been updated.
Do the selected Item's Name and Value properties match or is the SelectedItem an instance from within the same collection? .Net will not know to compare the items by name and value unless you tell it to, else it will use object equality to try and find the item in your list. If you are setting the selected item to an instance that is not actually in the list (but has the same properties) .net will not find it in the list. You have 2 options, override equality for your object and force comparison of properties, or ensure that you always set the selected item to an item in the list. Also try what Andy May suggested and do 2 way binding on the ItemsSource and on the SelectedItem, should work then