what am i missing in the implementation of the treeview ??
this is what is see:
View:
<TreeView Style="{StaticResource ExpandAllStyle}" ItemsSource="{Binding Titles}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type vm:TitleViewModel}">
<Grid ShowGridLines="False">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=DisplayedStartTime}" Margin="0,0,10,0" />
<TextBlock Grid.Column="1" Text="" />
<TextBlock Grid.Column="2" Text="{Binding Path=Text}" />
</Grid>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
my Repository:
public class TitleRepository : INotifyPropertyChanged
{
#region Titles
private ObservableCollectionEx<Title> _titles;
public ObservableCollectionEx<Title> Titles
{
get { return _titles; }
set
{
_titles = value;
OnPropertyChanged(new PropertyChangedEventArgs("Titles"));
}
}
#endregion
#region Current Title
private Title _currentTitle;
public Title CurrentTitle
{
get { return _currentTitle; }
set { _currentTitle = value; }
}
#endregion
public TitleRepository()
{
_titles = new ObservableCollectionEx<Title>();
_titles.Add(new Title("This is a title test"));
}
and my viewModel:
MainTreeViewModel:
public class MainTreeViewModel : ViewModelBase
{
private readonly TitleRepository _titleRepository;
private ObservableCollection<TitleViewModel> _titles;
public ObservableCollection<TitleViewModel> Titles
{
get { return _titles; }
set { _titles = value; }
}
public MainTreeViewModel()
{
_titleRepository = ((App)Application.Current).TitleRepository;
_titleRepository.Titles.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Titles_CollectionChanged);
_titles = new ObservableCollection<TitleViewModel>(
(from t in _titleRepository.Titles
select new TitleViewModel(t)).ToList());
}
void Titles_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
_titles.Add(new TitleViewModel(_titleRepository.Titles[_titleRepository.Titles.Count - 1]));
}
TitleViewModel:
public class TitleViewModel : ViewModelBase
{
readonly Title _title;
#region Displayed Start Time
public string DisplayedStartTime
{
get { return _title.DisplayedStartTime; }
}
#endregion
#region Text
public string Text
{
get { return _title.Text; }
}
#endregion
public TitleViewModel(Title title)
{
_title = title;
}
i bind the view model in the View.cs:
MainTreeViewModel vm = new MainTreeViewModel();
this.DataContext = vm;
i expect to see the title "This is a tile test" that i populated in the repository.
and it's a dynamic tree.
srry found the problem...
another binding in the MainWindow to the dataContext.
the MVVM pattern is valid and working.
Related
i have a custom userControl with DPs and have the problem that the binding to these properties only works if i use the controls outside of datatemplates.
Outside of Datatemplates works the usercontrol great.
XAML to Test the UserControl in a DataTemplate
<GroupBox Header="DataTemplate" Padding="5">
<ItemsControl ItemsSource="{Binding Dummies}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="local:Dummy">
<StackPanel Orientation="Horizontal" Margin="2">
<common:QuarterPicker SelectedFirstDay="{Binding Gueltigkeit}" Margin="5,0" />
<!--control the value of the item-->
<TextBlock Text="Gueltigkeit: "/>
<TextBlock Text="{Binding Gueltigkeit}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</GroupBox>
MainWindow codebehind and ViewModel
public partial class QuarterPickerTest : UserControl
{
public QuarterPickerTest()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
public class ViewModel
{
public ViewModel()
{
this.Dummy = new Dummy { Gueltigkeit = DateTime.Today };
this.Dummies = new List<Dummy>
{
new Dummy {Gueltigkeit = DateTime.Today},
new Dummy {Gueltigkeit = DateTime.Today.AddMonths(6)},
new Dummy {Gueltigkeit = DateTime.Today.AddDays(-6)},
};
}
public Dummy Dummy { get; set; }
public List<Dummy> Dummies { get; set; }
}
Here is the code behind of my UserControl
#region SelectedFirstDay
public DateTime SelectedFirstDay
{
get { return (DateTime)GetValue(SelectedFirstDayProperty); }
set { SetValue(SelectedFirstDayProperty, value); }
}
public static readonly DependencyProperty SelectedFirstDayProperty
= DependencyProperty.Register("SelectedFirstDay", typeof (DateTime), typeof (QuarterPicker),
new FrameworkPropertyMetadata(DateTime.Today, SelectedFirstDayPropertyChangedCallback) { BindsTwoWayByDefault = true });
private static void SelectedFirstDayPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
var quarterPicker = dependencyObject as QuarterPicker;
var date = (DateTime)args.NewValue;
var quarter = GetQuarter(date);
quarterPicker.UpdateProperties(date.Year, quarter);
if (date.Year == quarterPicker.LeftInfiniteYear && quarter == quarterPicker.LeftInfiniteQuarter)
quarterPicker.ShowPopupButtonLeftInfinite();
}
#endregion
Thanks for any suggestions!
I've just stuck in a problem to bind collection in ItemsControl with ItemTeplate that contains bounded ComboBox.
In my scenario I need to "generate" form that includes textbox and combobox for each item in collection and let user to update items. I could use DataGrid for that but I'd like to see all rows in edit mode, so I use ItemsControl with custom ItemTemplate.
It's ok to edit textboxes but when you try to change any ComboBox, all other ComboBoxes in other rows will change too.
Is it a bug or feature?
Thanks, Ondrej
Window.xaml
<Window x:Class="ComboInItemsControlSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="480" Width="640">
<Window.Resources>
<CollectionViewSource x:Key="cvsComboSource"
Source="{Binding Path=AvailableItemTypes}" />
<DataTemplate x:Key="ItemTemplate">
<Border BorderBrush="Black" BorderThickness="0.5" Margin="2">
<Grid Margin="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="20" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="{Binding Path=ItemValue}" />
<ComboBox Grid.Column="2"
SelectedValue="{Binding Path=ItemType}"
ItemsSource="{Binding Source={StaticResource cvsComboSource}}"
DisplayMemberPath="Name"
SelectedValuePath="Value" />
</Grid>
</Border>
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Path=SampleItems}"
ItemTemplate="{StaticResource ItemTemplate}"
Margin="10" />
</Grid>
Window.xaml.cs
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
public class ViewModel
{
public ViewModel()
{
SampleItems = new List<SampleItem> {
new SampleItem { ItemValue = "Value 1" },
new SampleItem { ItemValue = "Value 2" },
new SampleItem { ItemValue = "Value 3" }
};
AvailableItemTypes = new List<SampleItemType> {
new SampleItemType { Name = "Type 1", Value = 1 },
new SampleItemType { Name = "Type 2", Value = 2 },
new SampleItemType { Name = "Type 3", Value = 3 },
new SampleItemType { Name = "Type 4", Value = 4 }
};
}
public IList<SampleItem> SampleItems { get; private set; }
public IList<SampleItemType> AvailableItemTypes { get; private set; }
}
public class SampleItem : ObservableObject
{
private string _itemValue;
private int _itemType;
public string ItemValue
{
get { return _itemValue; }
set { _itemValue = value; RaisePropertyChanged("ItemValue"); }
}
public int ItemType
{
get { return _itemType; }
set { _itemType = value; RaisePropertyChanged("ItemType"); }
}
}
public class SampleItemType : ObservableObject
{
private string _name;
private int _value;
public string Name
{
get { return _name; }
set { _name = value; RaisePropertyChanged("Name"); }
}
public int Value
{
get { return _value; }
set { _value = value; RaisePropertyChanged("Value"); }
}
}
public abstract class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName) {
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Picture
here you can see the result on picture
I believe it's because you're binding to a CollectionViewSource, which tracks the current item. Try binding directly to your list instead, which won't track the current item
<ComboBox Grid.Column="2"
SelectedValue="{Binding Path=ItemType}"
DisplayMemberPath="Name"
SelectedValuePath="Value"
ItemsSource="{Binding RelativeSource={
RelativeSource AncestorType={x:Type ItemsControl}},
Path=DataContext.AvailableItemTypes}" />
While you have a combobox in each row, it doesnt see these comboboxes as being seperate. i.e. They are all using the same collection, and the same selectedValue, so when a value changes in one box, it changes in all of them.
The best way to fix this is to add the SampleItemType collection as a property on your SampleItem model and to then bind the combo box to that property.
I have a UserControl with ComboBox.
<ComboBox Grid.Row="8" Grid.Column="1"
VerticalAlignment="Center"
x:Name="cmbCategory"
ItemsSource="{Binding ElementName=ucAppiGeneralInfo, Path=Categories, Mode=TwoWay}"
SelectedItem="{Binding ElementName=ucAppiGeneralInfo, Path=SelectedCategory, Mode=TwoWay}"
IsEditable="True"
IsSynchronizedWithCurrentItem="True"
SelectedValuePath="CAT_ID"
TextSearch.TextPath="CAT_NAME">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=CAT_NAME}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding Path=PUBLIC_DESCRIPTION}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
The code behind is:
public partial class AppiGeneralInfoUC : UserControl
{
public DataTable Categories
{
get { return (DataTable)GetValue(CategoriesProperty); }
set { SetValue(CategoriesProperty, value);}
}
public static readonly DependencyProperty CategoriesProperty =
DependencyProperty.Register(
"Categories",
typeof(DataTable),
typeof(AppiGeneralInfoUC),
new UIPropertyMetadata(null));
public String SelectedCategory
{
get { return (String)GetValue(SelectedCategoryProperty); }
set
{
SetValue(SelectedCategoryProperty, value);
}
}
public static readonly DependencyProperty SelectedCategoryProperty =
DependencyProperty.Register(
"SelectedCategory",
typeof(String),
typeof(AppiGeneralInfoUC),
new UIPropertyMetadata(null));
public AppiGeneralInfoUC()
{
InitializeComponent();
}
}
I have a window which use the UserControl:
<TabControl>
<TabItem Header="Information">
<my:AppiGeneralInfoUC x:Name="ucAppiGeneralInfo"
Categories="{Binding Path=Categories, Mode=TwoWay}"
SelectedCategory="{Binding Path=SelectedCategory, Mode=TwoWay}" />
</TabItem>
the code behind is:
public partial class ApplicationWindow : Window
{
VMBase appiGeneralInfoWin = new AppiGeneralInfoVM();
public ApplicationWindow()
{
InitializeComponent();
ucAppiGeneralInfo.DataContext = appiGeneralInfoWin;
}
public void updateAction(string cat_id)
{
this.Title = "Update application";
(appiGeneralInfoWin as AppiGeneralInfoVM).setSelectedCategory(cat_id);
} ...
And finally I have ViewModel class:
class AppiGeneralInfoVM : VMBase
{
private DataTable categories = null;
private String selectedCategory = null;
public DataTable Categories
{
get { return this.categories; }
set
{
this.categories = value;
this.OnPropertyChanged("Categories");
}
}
public String SelectedCategory
{
get { return this.selectedCategory; }
set
{
this.selectedCategory = value;
this.OnPropertyChanged("SelectedCategory");
}
}
public AppiGeneralInfoVM()
{
ServicesLoader.LoadRunTimeServices();
Categories = GetService<CategoryBLL>().getCategories();
}
public void setSelectedCategory(string cat_id)
{
SelectedCategory = Categories.Select("cat_id =" + "'"+cat_id+"'")[0]["CAT_NAME"].ToString();
}
Everything works well but i have problem with the selectedItem (SelectedCategory),
it's not update at all....
I think it happens because your SelectedItem has string type while your collection is DataTable (which enumerates DataRow). Try changing your collection to be IEnumerable<string>
I have a ComboBox which is has an ItemTemplate applied on it and is bind to a List of entity return using linq. I'm using mvvm. It is bind to it successfully but when I set the selected value of it from code at runtime to show the selected value coming from db it doesn't select it. For reference here is my ComboBox xaml.
<DataTemplate x:Key="ManufacturerDataTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image x:Name="imgManufacturer" Width="25" Height="25"
Source="{Binding Path=ManufacturerImage}" Grid.Column="0"/>
<TextBlock x:Name="txtManufacturer" Grid.Column="1" HorizontalAlignment="Left"
VerticalAlignment="Center" Text="{Binding Path=ManufacturerName}"
Tag="{Binding Path=ManufacturerID}"/>
</Grid>
</DataTemplate>
<ComboBox x:Name="cboManufacturer"
SelectionChanged="cboManufacturer_SelectionChanged"
ItemsSource = "{Binding Path=CurrentManufacturers}"
SelectedValue="{Binding Path=SelectedManufacturer}"
Grid.Column="3" Grid.Row="2" Margin="20,9.25,68,7.75"
ItemTemplate="{StaticResource ManufacturerDataTemplate}" TabIndex="6"/>
Here is my part from code behind from viewModel.
List<tblManufacturer> currentManufacturers
= new List<tblManufacturer>();
tblManufacturer selectedManufacturer = null;
public List<tblManufacturer> CurrentManufacturers
{
get
{
return currentManufacturers;
}
set
{
currentManufacturers = value;
NotifyPropertyChanged("CurrentManufacturers");
}
}
public tblManufacturer SelectedManufacturer
{
get
{
return selectedManufacturer;
}
set
{
selectedManufacturer = currentManufacturers.Where(mm => mm.ManufacturerID == Convert.ToInt32(selectedDevice.tblManufacturer.EntityKey.EntityKeyValues[0].Value)).First();
NotifyPropertyChanged("SelectedManufacturer");
}
}
Here is the sample code snippet:
Xaml for ComboBox:
<ComboBox ItemsSource="{Binding ManufacturerList}" DisplayMemberPath="Name" SelectedValuePath="ID"
SelectedItem="{Binding SelectedManufacturer}"/>
ViewModel code :
public class Manufacturer
{
public int ID { get; set; }
public string Name { get; set; }
}
private List<Manufacturer> _manufactuerlist;
private Manufacturer _selectedManufacturer;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public Manufacturer SelectedManufacturer
{
get
{
return _selectedManufacturer;
}
set
{
_selectedManufacturer = value;
NotifyPropertyChanged("SelectedManufacturer");
}
}
public List<Manufacturer> ManufacturerList
{
get
{
return _manufactuerlist;
}
set
{
_manufactuerlist = value;
NotifyPropertyChanged("ManufacturerList");
}
}
And finally Set the Selected Manufacturer in your view model like this:
SelectedManufacturer = _manufactuerlist.Find(m => m.ID == 2);
I am trying to successfully TwoWay bind an ObservableCollection to TextBoxes in a DataTemplate. I can get the data to display properly, but I am unable to change the list data through the UI. I have a Model class named 'model' which contains an ObservableCollection named 'List'. The class implements the INotifyPropertyChanged interface. Here is the xaml for the shell. The DataContext for Window1's grid is set to "theGrid.DataContext=model"
<Window x:Class="BindThat.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BindThat"
Title="Window1" Height="300" Width="300">
<StackPanel x:Name="theGrid">
<GroupBox BorderBrush="LightGreen">
<GroupBox.Header>
<TextBlock Text="Group" />
</GroupBox.Header>
<ItemsControl ItemsSource="{Binding Path=List}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</GroupBox>
</StackPanel>
This is the code for the Model class:
class Model : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
private ObservableCollection<string> _list = new ObservableCollection<string>();
public ObservableCollection<string> List
{
get { return _list; }
set
{
_list = value;
NotifyPropertyChanged("List");
}
}
public Model()
{
List.Add("why");
List.Add("not");
List.Add("these?");
}
}
Could anyone advise if I am going about this the correct way?
You need a property to bind two way, so string is not good for this.
Wrap it in a string object, like this:
public class Model
{
public ObservableCollection<StringObject> List { get; private set; }
public Model()
{
List = new ObservableCollection<StringObject>
{
new StringObject {Value = "why"},
new StringObject {Value = "not"},
new StringObject {Value = "these"},
};
}
}
public class StringObject
{
public string Value { get; set; }
}
and bind to Value property instead of "."
Also, you don't need to notify of a change in observable collection, so until your model has some other propertis of its own, it does not need to have INotifyPropertyChange. If you want your ItemsControl react to changes in the individual StringObjects, then you should add INotifyPropertyChanged to a StringObject.
And yet again, two way binding is default, so you need only
<TextBox Text="{Binding Path=Value}" />
in your binding.
I believe you need to derive your collection items from DependencyObject for TwoWay binding to work. Something like:
public class DependencyString: DependencyObject {
public string Value {
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(string), typeof(DependencyString), new UIPropertyMetadata(""));
public override string ToString() {
return Value;
}
public DependencyString(string s) {
this.Value = s;
}
}
public class Model {
private ObservableCollection<DependencyString> _list = new ObservableCollection<DependencyString>();
public ObservableCollection<DependencyString> List {
get { return _list; }
}
public Model() {
List.Add(new DependencyString("why"));
List.Add(new DependencyString("not"));
List.Add(new DependencyString("these?"));
}
}
...
<StackPanel x:Name="theGrid">
<GroupBox BorderBrush="LightGreen">
<GroupBox.Header>
<TextBlock Text="Group" />
</GroupBox.Header>
<ItemsControl ItemsSource="{Binding Path=List}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Value, Mode=TwoWay}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</GroupBox>
</StackPanel>
xaml view:
<ItemsControl ItemsSource="{Binding List}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
in code behind in the constructor:
DataContext = new ViewModel();
in ViewModel Class:
class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
private ObservableCollection<StringObject> _List = new ObservableCollection<StringObject>();
public ObservableCollection<StringObject> List
{
get { return _List; }
set
{
_List = value;
NotifyPropertyChanged("List");
}
}
public ViewModel()
{
List = new ObservableCollection<StringObject>
{
new StringObject {Value = "why"},
new StringObject {Value = "not"},
new StringObject {Value = "these"}
};
}
}
public class StringObject
{
public string Value { get; set; }
}
Be careful with a collection with type string it doesn't work, you have to use an object => StringObject