I have a question regarding WPF binding using master-detail layout.
The master view contains a DevExpress GridControl with a TreeListView.
The detail view content is updated when the TreeListView selected item is changed (trough the binding).
The detail view contains just a ContentControl, with it's Content property bound to the current selected item of the TreeListView.
In order to display the detail view in a way that correspond to the type of the selected item, several DataTemplates are registered to the current application resources.
In my case, the DataTemplates are just one user control.
When the selection is changed in the master view TreeListView, the corresponding DataTemplate is correcly diplayed.
One of my detail view contains a DevExpress ListBoxEdit. The SelectedIndex is bound with UpdateSourceTrigger=PropertyChanged.
My problem is that the setter of the bound property is called with the value -1 when another item is selected in the master view.
It seems that the -1 default value is received when the control is unloaded or disposed or unbound. This screw up my view model.
Is there a proper way to avoid to be notified in the ViewModel when the control is unloaded?
[update]
The ContentControl that contains the detail view is defined like this:
<ContentControl Grid.Column="1" Content="{Binding CurrentApplicationSettings}" Margin="10,0,0,0"/>
In the corresponding view model (the SetValue method calls OnPropertyChanged):
public IApplicationSettingsViewModel CurrentApplicationSettings
{
get { return GetValue<IApplicationSettingsViewModel>(); }
set { SetValue(value); }
}
In the detail view where I have the problem, the ListBoxEdit is defined like this:
<dxe:ListBoxEdit ShowBorder="False" ItemsSource="{Binding AllLoggingLevels, Mode=OneTime}" SelectedIndex="{Binding Path=LoggingLevel, Mode=TwoWay, Converter={StaticResource LogLevelConverter}, UpdateSourceTrigger=PropertyChanged}">
<dxe:ListBoxEdit.StyleSettings>
<dxe:RadioListBoxEditStyleSettings/>
</dxe:ListBoxEdit.StyleSettings>
</dxe:ListBoxEdit>
In the corresponding ViewModel:
public LoggingLevel LoggingLevel
{
get { return GetValue<LoggingLevel>(); }
set { SetValue(value); }
}
The DataTemplates are registered by code:
public void RegisterDataTemplate(DataTemplate dataTemplate)
{
object dataTemplateKey = dataTemplate.DataTemplateKey;
if (dataTemplateKey == null)
{
throw new InvalidOperationException("The data template has an invalid key");
}
System.Windows.Application.Current.Resources.Add(dataTemplateKey, dataTemplate);
}
Thank you very much
Philippe
I have receive an answer from DevExpress support and this works for me.
"Having researched it, I found that this behavior occurs because of the internal editor's synchronization.When the editor loses its items source because of changing the focused row in the tree, it resets its properties such as EditValue, Text, SelectedItem, etc.
To prevent these properties from being synchronized, set the editor's AllowUpdateTwoWayBoundPropertiesOnSynchronization property to false."
Related
I have a WPF control inheriting from ComboBox whose ItemsSource is bound to a list of elements, and whose SelectedItem is bound to another field. I have two references to the same list in my application: one resides in my MainWindow class, and one resides in my App class.
The list of elements used as the ItemsSource is assigned to the ComboBox at the time it is created. Thus, I expect to get an ItemsSourceChanged event.
The strange thing is, if the ItemsSource is bound to the MainWindow's list, then the ComboBox becomes populated with the correct SelectedItem drawn from the bound field. However, if I bind the ItemsSource to the App's copy of the list, then the SelectedItem becomes overwritten with null due to the ItemsSourceChanged event.
Here is the XAML for the ComboBox when it's binding to the App copy of the list:
<local:TagSelectionComboBox FilteredTagsList="{Binding Path=TagsList, Source={x:Static Application.Current}}" SelectedItem="{Binding TagValue}"></local:TagSelectionComboBox>
Here is the XAML for the ComboBox when it's binding to the MainWindow copy of the list:
<local:TagSelectionComboBox FilteredTagsList="{Binding TagsList, RelativeSource={RelativeSource AncestorType=Window}}" SelectedItem="{Binding TagValue}"></local:TagSelectionComboBox>
Here is the MainWindow's property used for binding:
public ObservableCollection<Tag> TagsList
{
get { return proj.Sim.Tags; }
}
And here is the App's property used for binding:
public ObservableCollection<Tag> TagsList
{
get { return proj.Sim.Tags; }
}
So these two properties on App and MainWindow are returning the same list. Stepping through in the debugger confirms: proj.Sim.Tags contains the same list in both cases.
Why does changing from one binding source to another alter the binding behavior? Is something else going on?
I've found that if I explicitly set IsSynchronizedWithCurrentItem="True", then the behavior is the same in both cases. So it's almost like IsSynchronizedWithCurrentItem="True" is the default behavior when I use the MainWindow (RelativeSource) binding but IsSynchronizedWithCurrentItem="False" is the default behavior when I use the App (x:Static) binding.
Background: the ComboBox-derived control in this question is basically the same as YantingChen's answer for this question:
Dynamic filter of WPF combobox based on text input
I have an ObservableCollection of objects (e.g. Persons with First/Last Name) which I would like to display in an ItemsControl. Each Item is displayed in a custom "editor" control, which allows editing of the object's properties.
This part is working fine and fairly standard.
<ItemsControl ItemsSource="{Binding Persons}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<custom:PersonEditor Person="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
However, the custom editor control also has the ability to replace the entire object is has received (rather than just editing a person's name, replace it with a new person object).
What I am looking for is a way to push this change back into the ObservableCollection. As it is now, changing the Person object within the editor does not replace the item in the list, which would be the desired outcome.
Any help would be appreciated.
If you don't have access to the custom control itself what you could try would be using a setter on a property to clear the ObservableCollection and re-add the items instead of outright replacing it.
For example:
private ObservableCollection<MyModel> _dataSource;
public ObservableCollection<MyModel> DataSource
{
get
{
return _dataSource;
}
set
{
_dataSource.Clear();
foreach(var item in value)
{
_dataSource.Add(item);
}
}
}
This would prevent the item itself from getting replaced which causes issues with ItemSources since they apparently only bind to the items property once.
I'm using Silverlight's toolkit AutoCompleteBox control in a form where I want it to show the value bound to SelectedItem, or if it was null, then show the value bound to the Text property.
the problem is that when SelectedItem is null, it automatically clears the Text property, even when it was bound to a VM property that has a value.
here is some XAML:
<c:AutoCompleteBox
MinimumPopulateDelay="500"
ItemsSource="{Binding SuburbSearchResults}"
SelectedItem="{Binding SelectedSuburb}"
Text="{Binding SuburbText, Mode=OneWay}"
MinimumPrefixLength="3" />
First of all using Binding on Text and SelectedItem properties together ? Maybe it's not a good idea.
Its right when you use binding on SelectedItem this manages Text properties value for you.
If you use a ViewModel, I suggest you to bind one property of AutocompleteBox and just use SuburbText prop. in VM. (or just bind SelectedItem and you may use ValueMemberPath with it)
Edit 1:
//Suppose myVM.SuburbText is a local variable in VM, this shows Text prp. binding
//But I prefer Object binding with ValueMemberPath,you may use one of them
//But not both together
public string TextWillBeBound
{
get
{
if(SearchResults.SelectedItem!=null)
{
myVM.SuburbText=SearchResults.SelectedItem.TextProperty;
}
else if(myVM.SuburbText="")
{
myVM.SuburbText="Please write...";
}
return myVM.SuburbText;
}
set
{
if(SearchResults.SelectedItem==null)
{
myVm.SuburbText=value;
//with value you may create Suburb object ? and set as Selected.
//Depending on what you aim. I suggest using SelectedItem & ValueMemberPath
}
}
}
//How did I used this control before
//You can also bind SelectedItem and use ValueMemberPath as shown below.
<sdk:AutoCompleteBox MinimumPopulateDelay="500" MinimumPrefixLength="3"
Populating="AutoCompleteBox_Populating"
SelectedItem="{Binding Path=SELECTEDITEM,Mode=TwoWay}"
ValueMemberPath="DESCRIPTION">
I'm trying to build an usercontrol wich is able to take elements from XAML like this:
<ComboBox >
<ComboBoxItem />
<ComboBoxItem />
<ComboBoxItem />
</ComboBox>
In the ComboBox, you can just add the Items between the ComboBox tags, and I would like to copy this, but I don't know where to start.
Finished it should look like this:
<cis:ReportControl Grid.Row="3">
<cis:ReportItem />
</cis:ReportControl>
In the cis:ReportControl, there are some Buttons and a ComboBox, and basically I only want to feed the ComboBox with Items.
The Report Item is just a ComboBoxItem with some extra properties.
Edit:
I've implemented it according to #Snowbears answer, but the problem now is that the control has itself as an item.
I think this is because I have some content, and by defining the ContentProperty to my ComboBox, it is redirected into the Box.
What can I do to avoid this?
Edit II:
It fully works now with this:
private ItemCollection reportItems;
public ItemCollection ReportItems
{
get
{
if (reportItems == null)
{
reportItems = this.ComboBoxReports.Items;
}
return reportItems;
}
}
with the [ContentProperty("ReportItems")] Attribute. ComboBoxReports is the ComboBox in the Control, and I had to inherit from ItemsControl to get it to work.
You should create property in your UserControl which will expose something implementing IList interface. Let's say this property will be named ReportItems. This property should not have setter and it should be initialized in UserControl itself either in constructor in by field initialization on backing field.
UserControl should be marked with ContentProperty attribute with your property name (ReportItems)
Internal combobox should have it's ItemsSource bound to UserControl's ReportItems property
if you look for How to Create Your own Control, You must look for two things:
Custom Control 1 or User Control 1 (it's depend on your need)
Dependency Properties (use them in control)
I think you might need to use Custom-Control. Also you can inherit your Custom-Control from ComboBox or other Controls.
I am stucked at the part where I have to bind a collection to a dynamic usercontrol. Scenario is something like this.
I have a dynamic control, having a expander , datagrid, combobox and textbox, where combox and textbox are inside datagrid. There are already two collections with them. One is binded with combobox and another is binded with datagrid. When the item is changes in combox its respective value is set to its respective textbox, and so on. and this pair of value is then set to the collection binded with datagrid. A user can add multiple items.
Now the main problem is that all these things are happening inside a user control which is added dynamically, that is on button click event. A user can add desired numbers of user controls to the form.
problem is coming in this situtaion. Say I have added 3 controls. Now in 1st one if i add a code to the collection then it gets reflected in the next two controls too, as they are binded with same collection.
So, I want to know is there anyway to regenrate/rename the same collection so that the above condition should not arise.
It's hard to answer your question without seeing the bigger picture, however I have a feeling you are going about this the wrong way. It appears that you are adding instances of your user control directly from code. Instead of doing that, you should create some kind of ItemsControl in your XAML, and in its ItemTemplate have your user control. Bind that ItemsControl to a collection in your view model, and only manipulate that collection.
You should not be referring to visual controls in your view model or code behind. Whenever you find yourself referencing visual elements directly from code, it should raise a warning flag in your mind "Hey! There's a better way than that!"...
Example:
The view model:
public class ViewModel
{
public ObservableCollection<MyDataObject> MyDataObjects { get; set; }
public ViewModel()
{
MyDataObjects = new ObservableCollection<MyDataObject>
{
new MyDataObject { Name="Name1", Value="Value1" },
new MyDataObject { Name="Name2", Value="Value2" }
};
}
}
public class MyDataObject
{
public string Name { get; set; }
public string Value { get; set; }
}
The window XAML fragment containing the list box and the data template:
<Window.Resources>
...
<DataTemplate x:Key="MyDataTemplate">
<local:MyUserControl/>
</DataTemplate>
</Window.Resources>
...
<ListBox ItemsSource="{Binding MyDataObjects}"
ItemTemplate="{StaticResource MyDataTemplate}"
HorizontalContentAlignment="Stretch"/>
The user control:
<UniformGrid Rows="1">
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Value}" HorizontalAlignment="Right"/>
</UniformGrid>