i try to bind a wpf textbox to a dictionary placed in a viewmodel. The viewmodel is used as datacontext for the view.
I found a lot of examples and it sounds simple, but it will not work for me.
View:
TextBox x:Name="txbTest" Grid.Row="10" Grid.Column="2" Text="{Binding MyDict[First]}"
ViewModel:
public Dictionary<string, string> MyDict = new Dictionary<string, string>
{
{"First", "Test1"},
{"Second", "Test2"}
};
I try all variants i found
Text="{Binding MyDict[First]}"
Text="{Binding Path=MyDict[First]}"
Text="{Binding MyDict[First].Text}"
Text="{Binding MyDict[First].Value}"
But nothing works, textbox is empty. Any idea?
There is a Binding error in your code because MyDict is not a property. You have to bind to a Property and not to a Field
System.Windows.Data Error: 40 : BindingExpression path error: 'MyDict' property not found on 'object' ''MainWindow' (Name='')'. BindingExpression:Path=MyDict[First]; DataItem='MainWindow' (Name=''); target element is 'TextBox' (Name='textBox1'); target property is 'Text' (type 'String')
Change the MyDict Field to a Property like shown below
private Dictionary<string, string> _MyDict;
public Dictionary<string, string> MyDict
{
get { return _MyDict; }
set { _MyDict = value; }
}
In the constructor of your ViewModel initialize MyDict.
MyDict = new Dictionary<string, string>
{
{"First", "Test1"},
{"Second", "Test2"}
};
The following two variants will not work as MyDict["key"] returns a string and string does not have a Text or Value property. The other two variants should work.
Text="{Binding MyDict[First].Text}"
Text="{Binding MyDict[First].Value}"
The following bindings will work
Text="{Binding MyDict[First]}"
Text="{Binding Path=MyDict[First]}"
Related
I have a Listbox with property names of type Color from a MySettings object. I want to create another control (let's say a TextBox, the type is irrelevant) that binds to the value of property name listed.
I am at a loss as to how to achieve that. As it is now, the listbox is just a list of strings, obviously, it has no knowledge of the MySettings object.
One of the things I thought about is creating a collection of objects of a custom type that somehow binds to the MySettings object, and setting that as ItemsSource for the ListBox.
I know I trying to reinvent the wheel here, so I would appreciate any pointers to get me started.
Code-behind, ListBox is currently defined in xaml with no properties other than x:Name="listBox"
foreach (var prop in settings.GetType().GetProperties()
.Where(p => p.PropertyType == typeof(System.Drawing.Color))
.OrderBy(p => p.Name))
{
listBox.Items.Add(new ListBoxItem()
{
Content = prop.Name,
});
}
MySettings contains several properties:
[UserScopedSetting()]
[ConfigurationProperty(nameof(BackColor), DefaultValue = "GhostWhite")]
public System.Drawing.Color BackColor
{
get { return (System.Drawing.Color)base[nameof(BackColor)]; }
set
{
base[nameof(BackColor)] = value;
OnPropertyChanged();
}
}
[UserScopedSetting()]
[ConfigurationProperty(nameof(ForeColor), DefaultValue = "Black")]
public System.Drawing.Color ForeColor
{
get { return (System.Drawing.Color)base[nameof(ForeColor)]; }
set
{
base[nameof(ForeColor)] = value;
OnPropertyChanged();
}
}
You could assign the ListBox's ItemsSource to a collection of name/value pairs (an anonymous class here, but could as well be a regular class):
listBox.ItemsSource = settings.Properties
.OfType<SettingsProperty>()
.Where(p => p.PropertyType == typeof(System.Drawing.Color))
.Select(p => new
{
Name = p.Name,
Value = settings[p.Name]
});
Then declare a ListBox ItemTemplate that shows the Name property, and a TextBlock that binds to the Value property of the selected ListBox item:
<ListBox x:Name="listBox">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBlock Text="{Binding ElementName=listBox, Path=SelectedItem.Value}"/>
I'm binding an observsable collection of model objects to a data grid. But when I set the binding to the collection, I get a path error to the peoprties.
In debugging this issue, I've checked that the public properties in the CustomerModel are correctly named in the DataGrid binding. And also that the collection being returned to the model isn't empty. I also checked that the data context is set correctly in the View's code behind.
I think it might be an error due to the way I've specified the binding path in the xaml..
The full details of the binding error is as follows, for each field:
System.Windows.Data Error: 40 : BindingExpression path error: 'FirstName' property not found on 'object' ''MainViewModel' (HashCode=55615518)'. BindingExpression:Path=FirstName; DataItem='MainViewModel' (HashCode=55615518); target element is 'TextBox' (Name='fNameTbx'); target property is 'Text' (type 'String')
System.Windows.Data Error: 40 : BindingExpression path error: 'LastName' property not found on 'object' ''MainViewModel' (HashCode=55615518)'. BindingExpression:Path=LastName; DataItem='MainViewModel' (HashCode=55615518); target element is 'TextBox' (Name='lNameTbx'); target property is 'Text' (type 'String')
System.Windows.Data Error: 40 : BindingExpression path error: 'Email' property not found on 'object' ''MainViewModel' (HashCode=55615518)'. BindingExpression:Path=Email; DataItem='MainViewModel' (HashCode=55615518); target element is 'TextBox' (Name='emailTbx'); target property is 'Text' (type 'String')
Could anyone point me in the right direction, in order to debug this further?
DataGrid binding path and source are set as follows:
<DataGrid Name="infogrid"
Grid.Row="0"
Grid.RowSpan="3"
Grid.Column="1"
Grid.ColumnSpan="3"
AutoGenerateColumns="False"
ItemsSource="{Binding Customers}"
SelectedItem="{Binding SelectedCustomer}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Customers.Id}" Header="ID" />
<DataGridTextColumn Binding="{Binding Customers.FirstName}" Header="First Name" />
<DataGridTextColumn Binding="{Binding Customers.LastName}" Header="Last Name" />
<DataGridTextColumn Binding="{Binding Customers.Email}" Header="Email" />
</DataGrid.Columns>
</DataGrid>
The View Model contains an Observable collection of type CustomerModel, called Customers. This is what I've set the DataGrid ItemSource to. (I've removed other code from VM for readability)
namespace MongoDBApp.ViewModels
{
class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private ICustomerDataService _customerDataService;
public MainViewModel(ICustomerDataService customerDataService)
{
this._customerDataService = customerDataService;
QueryDataFromPersistence();
}
private ObservableCollection<CustomerModel> customers;
public ObservableCollection<CustomerModel> Customers
{
get
{
return customers;
}
set
{
customers = value;
RaisePropertyChanged("Customers");
}
}
private void QueryDataFromPersistence()
{
Customers = _customerDataService.GetAllCustomers().ToObservableCollection();
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
And these are the fields that in the CustomerModel, so not sure why the properties are not being found during binding:
public class CustomerModel : INotifyPropertyChanged
{
private ObjectId id;
private string firstName;
private string lastName;
private string email;
[BsonElement]
ObservableCollection<CustomerModel> customers { get; set; }
/// <summary>
/// This attribute is used to map the Id property to the ObjectId in the collection
/// </summary>
[BsonId]
public ObjectId Id { get; set; }
[BsonElement("firstName")]
public string FirstName
{
get
{
return firstName;
}
set
{
firstName = value;
RaisePropertyChanged("FirstName");
}
}
[BsonElement("lastName")]
public string LastName
{
get
{
return lastName;
}
set
{
lastName = value;
RaisePropertyChanged("LastName");
}
}
[BsonElement("email")]
public string Email
{
get
{
return email;
}
set
{
email = value;
RaisePropertyChanged("Email");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
This is how the data context is set in the View's code behind:
public partial class MainView : Window
{
private MainViewModel ViewModel { get; set; }
private static ICustomerDataService customerDataService = new CustomerDataService(CustomerRepository.Instance);
public MainView()
{
InitializeComponent();
ViewModel = new MainViewModel(customerDataService);
this.DataContext = ViewModel;
}
}
These binding errors are not related to your DataGrid.
They indicate that you have 3 TextBoxes somewhere of the names fNameTbx, lNameTbx, and emailTbx. A DataGrid does not generate it's items with a Name property, so it is not the one causing these binding errors.
When trying to read binding errors, it's best to break them up by semi-colons and read them backwards, as demonstrated here.
For example,
System.Windows.Data Error: 40 : BindingExpression path error: 'FirstName' property not found on 'object' ''MainViewModel' (HashCode=55615518)'. BindingExpression:Path=FirstName; DataItem='MainViewModel' (HashCode=55615518); target element is 'TextBox' (Name='fNameTbx'); target property is 'Text' (type 'String')
Can also be read as
target property is 'Text' (type 'String')
target element is 'TextBox' (Name='fNameTbx');
DataItem='MainViewModel' (HashCode=55615518);
BindingExpression path error: 'FirstName' property not found on 'object' ''MainViewModel' (HashCode=55615518)'. BindingExpression:Path=FirstName;
Meaning somewhere you have
<TextBox Name="fNameTbx" Text="{Binding FirstName}" />
Where the DataContext of this TextBox is of type MainViewModel. And MainViewModel does not have a property of FirstName.
I'd recommend searching your project for those names, or you could use a tool like Snoop to debug databindings and DataContext issues at runtime.
The exceptions indicate that the DataBinding engine is looking for the fields FirstName, LastName, etc. on MainViewModel as opposed to CustomerModel.
You don't need to specify the property Customers in the individual binding expressions for the columns:
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Id}" Header="ID" />
<DataGridTextColumn Binding="{Binding FirstName}" Header="First Name" />
<DataGridTextColumn Binding="{Binding LastName}" Header="Last Name" />
<DataGridTextColumn Binding="{Binding Email}" Header="Email" />
</DataGrid.Columns>
I was having the same issue when I had the TextBlock Text Binding inside of a DataTemplate and I ended up having to do:
Text={Binding DataContext.SuccessTxt}
to get it to work properly. Try adding "DataContext." in front of the property and see if that works.
public Window()
{
this.DataContext = this;
InitializeComponent();
}
public string Name {get;set;}
//xaml
<TextBlock Text="{Binding Name}"/>
this.DataContext = this; before InitializeComponent();
(DataContext need available before load xaml in InitializeComponent())
Properties Name should be public and { get; }
(If private then wpf can't access)
I want to bind an IEnumerable to an ItemsControl. Here is the code in XAML that works:
<ItemsControl Name="SearchItemsControl" ItemsSource="{Binding Path=SearchResult}" ScrollViewer.CanContentScroll="True" BorderThickness="0" Background="{StaticResource PopUpContentGradientBrush}" VirtualizingStackPanel.IsVirtualizing="True" >
I want to do it from code-behind, and this is my code:
Binding binding = new Binding("SearchResult");
binding.Source = SearchResult;
And in BeginInvoke of a dispatcher:
SearchItemsControl.SetBinding(ItemsControl.ItemsSourceProperty, binding);
Here's the error I get in the Otput tab of VS:
System.Windows.Data Error: 40 : BindingExpression path error: 'SearchResult' property not found on 'object' ''WhereSelectEnumerableIterator`2' (HashCode=14814649)'. BindingExpression:Path=SearchResult; DataItem='WhereSelectEnumerableIterator`2' (HashCode=14814649); target element is 'ItemsControl' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
SearchResult is a property in my View-Model, which is of type IEnumerable. The control's name in XAML is SearchItemsControl.
Why is this code not working?
Here's the property:
private IEnumerable<SearchResultModel> _searchResult;
public IEnumerable<SearchResultModel> SearchResult
{
get { return _searchResult; }
set
{
_searchResult = value;
OnPropertyChanged("SearchResult");
}
}
First, SearchResult was ObservableCollection, but the same error appeared and I changed it to IEnumaberable.
you have to remove
binding.Source = SearchResult;
otherwise it mean that your ItemsControl get a new "DataContext" SearchResult and should bind the ItemsSource to a Property SearchResult of your object SearchResult.
edit: the following would work but is not the same as you did in xaml
Binding binding = new Binding(".");
binding.Source = SearchResult;
I am trying to bind a ComboBox to the named cells of a SpreadsheetGear worksheet.
SpreadsheetGear is an obfuscated assembly, so that i my first guess.
<ComboBox Width="200" x:Name="comboBox" IsEditable="True" ItemsSource="{Binding Names, Mode=OneWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
and the view-model propery is
private IWorksheet worksheet;
public IWorksheet Worksheet
{
get { return worksheet; }
private set { SetField(ref worksheet, value, () => Worksheet); OnPropertyChanged(() => Names); }
}
public IEnumerable<IName> Names
{
get { return Worksheet.Names.Cast<IName>(); }
}
I am getting the following error in my Output window
System.Windows.Data Error: 40 : BindingExpression path error: 'Name' property not found on 'object' ''ᜪ' (HashCode=1500138080)'. BindingExpression:Path=Name; DataItem='ᜪ' (HashCode=1500138080); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
I've tried returning Worksheet.Names directly, which doesn't inherit from Enumerable but DOES provide GetEnumerator(). That yielded the same error.
Any ideas?
Without more code, it's hard to say, but I'll take a random guess: Is IName an internal interface? Most code obfuscators will only mangle internal/private/protected classes/enums/interfaces...
I try to use binding with an attached property. But can't get it working.
public class Attached
{
public static DependencyProperty TestProperty =
DependencyProperty.RegisterAttached("TestProperty", typeof(bool), typeof(Attached),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Inherits));
public static bool GetTest(DependencyObject obj)
{
return (bool)obj.GetValue(TestProperty);
}
public static void SetTest(DependencyObject obj, bool value)
{
obj.SetValue(TestProperty, value);
}
}
The XAML Code:
<Window ...>
<StackPanel local:Attached.Test="true" x:Name="f">
<CheckBox local:Attached.Test="true" IsChecked="{Binding (local:Attached.Test), Mode=TwoWay, RelativeSource={RelativeSource Self}}" />
<CheckBox local:Attached.Test="true" IsChecked="{Binding (local:Attached.Test), Mode=TwoWay}" />
</StackPanel>
</Window>
And the Binding Error:
System.Windows.Data Error: 40 : BindingExpression path error: '(local:Attached.Test)' property not found on 'object' ''StackPanel' (Name='f')'. BindingExpression:Path=(local:Attached.Test); DataItem='StackPanel' (Name='f'); target element is 'CheckBox' (Name=''); target property is 'IsChecked' (type 'Nullable`1')
Believe it or not, just add Path= and use parenthesis when binding to an attached property:
IsChecked="{Binding Path=(local:Attached.Test), Mode=TwoWay, RelativeSource={RelativeSource Self}}"
In addition, your call to RegisterAttached should pass in "Test" as the property name, not "TestProperty".
I'd have preferred to post this as a comment on Kent's answer but since I don't have enough rep to do so... just wanted to point out that as of WPF 4.5, adding Path= isn't necessary anymore. However the attached property name still needs to be wrapped with parentheses.
Putting a bracket works. I had to do automation id binding of a parent contentcontrol to a textblock in datatemplate. Automation Id is an attached property.
I put the property in brackets and binding worked.
AutomationProperties.AutomationId="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContentControl},Path=(AutomationProperties.AutomationId)}"