I am a beginner in programming, especially WPF. I have an application in WPF. I have changed connection to .sdf database from Entity Framework to SqlCeCommand. Unfortunatelly, before this I had the following code for binding a ComboBox.
<DockPanel Grid.Row="4">
<Button x:Name="LoadButton" Height="20" ToolTip="Choose setting name to load" Width="75" Padding="2,2,2,2" Margin="2,0,2,0" HorizontalAlignment="Left" VerticalAlignment="Center" Content="Load Settings" Command="{Binding LoadSettingsCommand}"/>
<ComboBox x:Name="LoadSettingsComboBox" ToolTip="Choose setting name to load" ItemsSource="{Binding Mode=OneWay, Path=Settings/}" SelectedValue="{Binding LoadSettingName, Mode=OneWayToSource}" SelectedValuePath="Name" Grid.Column="1" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock HorizontalAlignment="Center" Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DockPanel>
and:
List<Setting> _settings;
Settings = new CollectionView(_settings);
And it worked. After changing connection to DataBase there is no error, but ComboBox doesn't show any data. Before Setting class was generated by entity framework. Now, I made my own class Setting. What this class should implement? Can you help me?
Vote this answer if you find it helpful.
As per my understanding if you are using Setting as data object you need to store it into ObservableCollection<>. Use like this :
private ObservableCollection<Settings> _settingList = new ObservableCollection<Settings>();
public ObservableCollection<Settings> SettingList
{
get
{
return this._settingList;
}
set
{
if(value==null)
return;
this._settingList = value;
//OnPropertyChanged(()=>this.SettingList); //It is not required as ObservableCollection<> itself notifies on collection changed.
}
}
If you are implementing your own Setting class then you should implement INotifyPropertyChanged interface to bind properties with combobox item. Below is the code for your reference:
public class SettingsModel : INotifyPropertyChanged
{
#region INotifyPropertyChanded event Implementation
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged<TProperty>(Expression<Func<TProperty>> propertyExpression)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null == handler)
return;
if (null != propertyExpression)
{
var e = new PropertyChangedEventArgs(((MemberExpression)propertyExpression.Body).Member.Name);
handler(this, e);
}
}
public void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != PropertyChanged)
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Above implementation gives you a method OnPropertyChanged(string propertyName). You need to call this method on "set" section of your each property in your class. Whenever property value gets changed it will notify to the DependencyProperty of control.
Hope this will help you.
Related
I'm trying to bind the data with text block in a listview but it doesn't show the data. However, when I run the application, I can see the list is created but it display the empty data. See the empty list of data in the below image:
Here's my XAML code:
<ListView Margin="20,0,0,20" x:Name="listView_attachedFiles">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Width="100" Text="{Binding AttachedFiles, Mode=TwoWay, NotifyOnTargetUpdated=True}">
<Hyperlink>
</Hyperlink>
</TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Here's the C# Code
public class ListviewModel : INotifyPropertyChanged
{
private ObservableCollection<string> _attachedfiles;
public ObservableCollection<string> AttachedFiles
{
get { return _attachedfiles; }
set
{
_attachedfiles = value;
OnPropertyChanged("AttachedFiles");
}
}
public ListviewModel()
{
AttachedFiles = new ObservableCollection<string>();
}
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Here's is how I'm populating the data:
// I'm calling this method to attach the list of filenames
private void ExecuteMethod_AttachFile(object Parameter)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Multiselect = true;
if (ofd.ShowDialog() == true)
{
AttachedFileLocation = ofd.FileNames.ToList();
// adding bind data to the list of viewmodel: adding attachefile names
foreach (var file in AttachedFileLocation)
{
FileInfo info = new FileInfo(file);
DMS_Form.Instance.ListofViewModel.AttachedFiles.Add(info.Name + " X");
}
}
}
Here's is how I'm attaching the itemssource reference in the constructor of the WPF Form.
//Binding the list of attached files with the listview
listView_attachedFiles.ItemsSource = ListofViewModel.AttachedFiles;
Please help me figure out where I'm making the mistake. Any help would be highly appreciated. Thank you
Ali
With some hours of research, I realized there was a bit problem with my code. The solution is to first define data context in the WPF Form constructor method i.e
listView_attachedFiles.DataContext = ListofViewModel;
second update this XAML code
<TextBlock Width="100" Text="{Binding AttachedFiles, Mode=TwoWay, NotifyOnTargetUpdated=True}">
with this XAML code.
<TextBlock Width="100" Text="{Binding}">
Now save, and build the application, run it.
I have an ItemsControl that should display the values of some properties of an object.
The ItemsSource of the ItemsControl is an object with two properties: Instance and PropertyName.
What I am trying to do is displaying all the property values of the Instance object, but I do not find a way to set the Path of the binding to the PropertyName value:
<ItemsControl ItemsSource={Binding Path=InstanceProperties}>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=PropertyName, Mode=OneWay}"/>
<TextBlock Text=": "/>
<TextBlock Text="{Binding Source=??{Binding Path=Instance}??, Path=??PropertyName??, Mode=OneWay}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
the question marks are the points where I don't know how to create the binding.
I initially tried with a MultiValueConverter:
<TextBlock Grid.Column="1" Text="{Binding}">
<TextBlock.DataContext>
<MultiBinding Converter="{StaticResource getPropertyValue}">
<Binding Path="Instance" Mode="OneWay"/>
<Binding Path="PropertyName" Mode="OneWay"/>
</MultiBinding>
</TextBlock.DataContext>
</TextBlock>
The MultiValueConverter uses Reflection to look through the Instance and returns the value of the property.
But if the property value changes, this change is not notified and the displayed value remains unchanged.
I am looking for a way to do it with XAML only, if possible, if not I will have to write a wrapper class to for the items of the ItemsSource collection, and I know how to do it, but, since it will be a recurring task in my project, it will be quite expensive.
Edit:
For those who asked, InstanceProperties is a property on the ViewModel which exposes a collection of objects like this:
public class InstanceProperty : INotifyPropertyChanged
{
//[.... INotifyPropertyChanged implementation ....]
public INotifyPropertyChanged Instance { get; set; }
public string PropertyName { get; set; }
}
Obviously the two properties notify theirs value is changing through INotifyPropertyChanged, I don't include the OnPropertyChanged event handling for simplicity.
The collection is populated with a limited set of properties which I must present to the user, and I can't use a PropertyGrid because I need to filter the properties that I have to show, and these properties must be presented in a graphically richer way.
Thanks
Ok, thanks to #GazTheDestroyer comment:
#GazTheDestroyer wrote: I cannot think of any way to dynamically iterate and bind to an arbitrary object's properties in XAML only. You need to write a VM or behaviour to do this so you can watch for change notifications, but do it in a generic way using reflection you can just reuse it throughout your project
I found a solution: editing the ViewModel class InstanceProperty like this
added a PropertyValue property
listen to PropertyChanged event on Instance and when the PropertyName value changed is fired, raise PropertyChanged on PropertyValue
When Instance or PropertyName changes, save a reference to Reflection's PropertyInfo that will be used by PropertyValue to read the value
here is the new, complete, ViewModel class:
public class InstanceProperty : INotifyPropertyChanged
{
#region Properties and events
public event PropertyChangedEventHandler PropertyChanged;
private INotifyPropertyChanged FInstance = null;
public INotifyPropertyChanged Instance
{
get { return this.FInstance; }
set
{
if (this.FInstance != null) this.FInstance.PropertyChanged -= Instance_PropertyChanged;
this.FInstance = value;
if (this.FInstance != null) this.FInstance.PropertyChanged += Instance_PropertyChanged;
this.CheckProperty();
}
}
private string FPropertyName = null;
public string PropertyName
{
get { return this.FPropertyName; }
set
{
this.FPropertyName = value;
this.CheckProperty();
}
}
private System.Reflection.PropertyInfo Property = null;
public object PropertyValue
{
get { return this.Property?.GetValue(this.Instance, null); }
}
#endregion
#region Private methods
private void CheckProperty()
{
if (this.Instance == null || string.IsNullOrEmpty(this.PropertyName))
{
this.Property = null;
}
else
{
this.Property = this.Instance.GetType().GetProperty(this.PropertyName);
}
this.RaisePropertyChanged(nameof(PropertyValue));
}
private void Instance_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == this.PropertyName)
{
this.RaisePropertyChanged(nameof(PropertyValue));
}
}
private void RaisePropertyChanged(string propertyname)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
#endregion
}
and here is the XAML:
<ItemsControl ItemsSource={Binding Path=InstanceProperties}>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=PropertyName, Mode=OneWay}"/>
<TextBlock Text=": "/>
<TextBlock Text="{Binding Path=PropertyValue, Mode=OneWay}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I have this Custom Control
XAML:
<UserControl x:Class="WpfApplication1.UC"
...
x:Name="uc">
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Orientation="Horizontal">
<TextBox Text="{Binding Test, ElementName=uc}" Width="50" HorizontalAlignment="Left"/>
</StackPanel>
</UserControl>
C#
public partial class UC : UserControl
{
public static readonly DependencyProperty TestProperty;
public string Test
{
get
{
return (string)GetValue(TestProperty);
}
set
{
SetValue(TestProperty, value);
}
}
static UC()
{
TestProperty = DependencyProperty.Register("Test",typeof(string),
typeof(UC), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
}
public UC()
{
InitializeComponent();
}
}
And this is how i used that custom control:
<DockPanel>
<ItemsControl ItemsSource="{Binding Path=DataList}"
DockPanel.Dock="Left">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}" CommandParameter="{Binding}" Click="Button_Click"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<local:UC Test="{Binding SelectedString, Mode=OneWay}"/>
</DockPanel>
--
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
private ObservableCollection<string> _dataList;
public ObservableCollection<string> DataList
{
get { return _dataList; }
set
{
_dataList = value;
OnPropertyChanged("DataList");
}
}
private string _selectedString;
public string SelectedString
{
get { return _selectedString; }
set
{
_selectedString = value;
OnPropertyChanged("SelectedString");
}
}
public MainWindow()
{
InitializeComponent();
this.DataList = new ObservableCollection<string>();
this.DataList.Add("1111");
this.DataList.Add("2222");
this.DataList.Add("3333");
this.DataList.Add("4444");
this.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.SelectedString = (sender as Button).CommandParameter.ToString();
}
}
If I do not change text of UC, everything is ok. When I click each button in the left panel, button's content is displayed on UC.
But when I change text of UC (ex: to 9999), Test property lost binding. When I click each button in the left panel, text of UC is the same that was changed (9999). In debug I see that SelectedString is changed by each button click but UC's text is not.
I can 'fix' this problem by using this <TextBox Text="{Binding Test, ElementName=uc, Mode=OneWay}" Width="50" HorizontalAlignment="Left"/> in the UC.
But I just want to understand the problem, can someone help me to explain it please.
Setting the value of the target of a OneWay binding clears the binding. The binding <TextBox Text="{Binding Test, ElementName=uc}" is two way, and when the text changes it updates the Test property as well. But the Test property is the Target of a OneWay binding, and that binding is cleared.
Your 'fix' works because as a OneWay binding, it never updates Test and the binding is never cleared. Depending on what you want, you could also change the UC binding to <local:UC Test="{Binding SelectedString, Mode=TwoWay}"/> Two Way bindings are not cleared when the source or target is updated through another method.
The issue is with below line
<local:UC Test="{Binding SelectedString, Mode=OneWay}"/>
The mode is set as oneway for SelectString binding so text will be updated when the value from code base changes. To change either the source property or the target property to automatically update the binding source as TwoWay.
<local:UC Test="{Binding SelectedString, Mode=TwoWay}"/>
New to WPF, MVVM, data binding, and Entity Framework, so apologies in advance.
I'm attempting to bind WPF controls to my database-generated model objects through a viewmodel. I'm able to change database values by typing in the textbox, but any direct changes to the database rows do not seem to fire off the PropertyChanged event. At least they are not reflected in my viewmodel objects. I've implemented iNotifyPropertyChanged on my Entity Framework generated classes thus:
public partial class GenGround : INotifyPropertyChanged
{
public double ClMax
{
get { return (double)this.clMax; }
set
{
this.clMax = (float)value;
MainWindow.db.SaveChanges();
OnPropertyChanged("ClMax");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string Property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(Property));
}
}
}
I have a listbox:
<DataTemplate x:Key="missionLegTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
<ListBox x:Name="missionList" SelectionChanged="missionList_SelectionChanged" Grid.ColumnSpan="2" ItemsSource="{Binding Mode=OneWay}" ItemTemplate="{DynamicResource missionLegTemplate}" />
private void MainWindow1_Loaded(object sender, RoutedEventArgs e)
{
this.missionList.ItemsSource = _CurrentMissionProfile.MissionLegs;
}
"MissionLegs" is an ObservableCollection of MissionLeg objects, attached to the database. The selected item of this listbox should tell the textboxes what properties to get and set. Textbox:
<TextBox x:Name="velocityBox" Text="{Binding Mode=TwoWay,Path=SelectedItem.Velocity, ElementName=missionList}" IsEnabled="False" />
As I said, this seems to write to the database, but when I make changes to the corresponding row in SSMS, nothing seems to happen. Ideas?
Hi i am using this code in myClass to change content in my wpf applciation
public event PropertyChangedEventHandler PropertyChanged;
protected void Notify(string propertyName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Everytime i change a property in myClass it changes the labels that i have in my app.
<Label Content="{Binding Category}" Padding="7,0,0,0" />
this works just fine but in myClass i have an property contains an ilist to another class Article
private IList<Article> m_articles = new List<Article>();
Now to myquestion the Notify method doesnt update the content in my the Ilist is there i way to make it update with an ilist and view. All property in myclass works fine if it is an string or int but when it is a Ilist it wont update. Hope you guys understand what i mean my english is bad sry..
Thanks for help
here the code in xaml
<ListBox Name="ArtInfo" ItemsSource="{Binding Path=Articles}">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Artnr}" />
</DataTemplate>
</ListBox.ItemTemplate>
{Binding Path=Articles} <-- this is the property that contains an ilist
<-- this is an property in the Article class
You should use an ObservableCollection<Article> instead of a List<Article>