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>
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 got a datagrid which is bound to an ObservableCollection of the ViewModel below. The Datagrid will display all values correctly, so the binding seems to be working, but if I change some value the Grid won't call the setter's of my VM. Could somebody tell me why?
Here's my ViewModel:
public class DocumentVm : ViewModelBase
{
private Document document;
public bool IsNew { get; private set; }
public Document Document {
get { return document; }
}
public DocumentVm(Document document)
{
this.document = document;
IsNew = false;
}
public DocumentVm(Document document, bool isNew)
{
this.document = document;
IsNew = isNew;
}
public String Name
{
get { return document.Name; }
set { document.Name = value; RaisePropertyChangedEvent("Name");}
}
public String Path
{
get { return document.Path; }
set { document.Path = value; }
}
public String Metadata
{
get { return document.Metadata; }
set { document.Metadata = value; }
}
public int SpeechId
{
get { return document.SpeechId; }
set { document.SpeechId = value; }
}
}
Here is the XAML Code:
<DataGrid Margin="3" Grid.Row="7" Grid.Column="1" BorderThickness="0"
ItemsSource="{Binding Path=CurrentSpeech.Documents, Mode=TwoWay}"
SelectedItem="{Binding Path=CurrentSpeech.CurrentDocument, Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Name" Width="SizeToCells">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Name, Mode=TwoWay}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="MetaDaten" Width="SizeToCells">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Metadata, Mode=TwoWay}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Pfad" Width="SizeToCells">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
Thank you all!
UpdateSourceTrigger=PropertyChanged on the Bindings was missing.
Not sure why the setters aren't getting called but if you use dependency properties they actually do get called. I'm far too drunk to investigate why the CLR properties aren't getting set in your project but this works for me.
public partial class MainWindow : Window
{ public ObservableCollection<DocumentVm> Items
{
get { return (ObservableCollection<DocumentVm>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(ObservableCollection<DocumentVm>), typeof(MainWindow), new PropertyMetadata(null));
public MainWindow()
{
InitializeComponent();
Items = new ObservableCollection<DocumentVm>();
Items.Add(new DocumentVm() { Name = "Name 1"});
Items.Add(new DocumentVm() { Name = "Name 2"});
}
}
public class DocumentVm : DependencyObject
{
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(DocumentVm), new PropertyMetadata(null, new PropertyChangedCallback( (s, e)=>
{
// This will run when the property is set.
})));
}
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.
I just can't make the following situation work:
I have a class, with the following implementation:
public class SelectionItem<T> : ViewModelBase where T : Entity
{
private bool? _isSelected;
public bool? IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
RaisePropertyChanged("IsSelected");
}
}
public T Item { get; set; }
}
And I have the following property on my ViewModel:
private IEnumerable<SelectionItem<DB_Aux_Pessoas>> _vendedores;
public IEnumerable<SelectionItem<DB_Aux_Pessoas>> Vendedores
{
get
{
return _vendedores;
}
set
{
_vendedores = value;
RaisePropertyChanged("Vendedores");
}
}
Then, in my View, I have the ComboBox:
<ComboBox Margin="3,0,0,0"
Height="23"
Width="200"
ItemsSource="{Binding Vendedores, Mode=TwoWay}"
Grid.Column="1"
Grid.Row="1"
HorizontalAlignment="Left">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}" />
<TextBlock Text="{Binding Item.NomeRazaoSocial}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
But when I change the CheckBox on the ComboBoxItem, it does not reflect on the property.
The code for DB_Aux_Pessoas is below:
[MetadataTypeAttribute(typeof(DB_Aux_Pessoas.DB_Aux_PessoasMetadata))]
public partial class DB_Aux_Pessoas
{
// This class allows you to attach custom attributes to properties
// of the DB_Aux_Pessoas class.
//
// For example, the following marks the Xyz property as a
// required property and specifies the format for valid values:
// [Required]
// [RegularExpression("[A-Z][A-Za-z0-9]*")]
// [StringLength(32)]
// public string Xyz { get; set; }
internal sealed class DB_Aux_PessoasMetadata
{
// Metadata classes are not meant to be instantiated.
private DB_Aux_PessoasMetadata()
{
}
public Nullable<short> Cliente { get; set; }
public string Id_Numero { get; set; }
public string NomeRazaoSocial { get; set; }
public Nullable<short> Supervisor { get; set; }
public Nullable<short> Vendedor { get; set; }
}
}
What I am doing wrong here?
Tks in advance.
I can't check this right now, but I'm fairly sure the dependency property IsChecked will only accept a binding to a bool and NOT a Nullable<bool>. You might have to do the conversion in your viewmodel and decide which should be the appropriate default for a null value.
This works fine for me. Add an extra property to give you some visual feedback like this:
public class SelectionItem<T> : ViewModelBase
{
private bool? _isSelected;
public bool? IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
RaisePropertyChanged("IsSelected");
RaisePropertyChanged("Feedback");
}
}
public string Feedback
{
get
{
if (!this.IsSelected.HasValue)
{
return "null";
}
if (this.IsSelected.Value)
{
return "yes";
}
return "no";
}
}
public T Item { get; set; }
}
And in your xaml, add an extra TextBlock to show the result:
<ComboBox Grid.Column="1"
Grid.Row="1"
Height="23"
Margin="3,0,0,0"
Width="200"
HorizontalAlignment="Left"
ItemsSource="{Binding Vendedores, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}" />
<TextBlock Text="{Binding Item.NomeRazaoSocial}" />
<TextBlock Text="{Binding Feedback}" Margin="5,0,0,0" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
If you see "Yes", "No", "Null" changing as you click the checkboxes, then it's working. Your code worked for me.
public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.RegisterAttached("IsChecked", typeof(bool), typeof(...),
new PropertyMetadata(false, delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
...
}
IsChecked is bool. You can possibly track BindingExpression Error in Output window.
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