Binding in code-behind - wpf

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;

Related

WPF: How to get SelectedItem after applying value converter?

I have control where I bind the images. The code in my view model looks as following:
public List<IDocument> SelectedEventPhotoList
{
get { return _selectedEventPhotoList; }
set
{
if (Equals(value, _selectedEventPhotoList))
return;
_selectedEventPhotoList = value;
RaisePropertyChanged(() => SelectedEventPhotoList);
}
}
public IDocument SelectedEventPhoto
{
get { return _selectedEventPhoto; }
set
{
if (Equals(value, _selectedEventPhoto))
return;
_selectedEventPhoto = value;
RaisePropertyChanged(() => SelectedEventPhoto);
}
}
The binding looks as following:
<ListView Grid.Row="0"
ItemsSource="{Binding SelectedEventPhotoList, Converter={StaticResource PathToFileConverter}}"
SelectedItem="{Binding SelectedEventPhoto}"
As you can see I have a list of IDocument types to bind to ItemsSource and SelectedItem is of IDocument type. But, images have Source property that is of type string and I've used PathToFileConverter value converter to convert IDocument types to strings.
The issue is in fact that after using converter, SelectedItem is null.
How can I achieve the SelectedItem keeps IDocument type, which is not null?
You should apply the converter to the Source property binding of the Image and not to the ItemsSource of the ListView
The type of the property that is bound to the SelectedItem property of a ListView should always be T if the ItemsSource property is bound or set to an IEnumerable<T>.
You should remove the converter from the ItemSource binding, and add DisplayMemberPath="PathOfFileProperty", which 'PathOfFileProperty' is the string property on IDocument which indicate the file path

How to bind List<string> to a DependencyProperty

I'm trying to learn about DependencyProperty. To do so I want to create a new UserControl which displays a list.
The location of this list must exist in the parent as a property. For this, I only have MainWindow, MainWindowViewModel (these are the parent) and the UserControl (the child) (which is currently using code behind).
In my MainWindow I have
<Grid>
<uc:RecentList MessageList="{Binding Messages}" />
</Grid>
And in the code behind
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
And the ViewModel
public MainWindowViewModel()
{
this.Messages = new ObservableCollection<string>();
this.Messages.Add("Item 1");
this.Messages.Add("Item 2");
this.T = "hi";
}
public ObservableCollection<string> Messages { get; set; }
In the UserControl I have
<Grid>
<ListView ItemsSource="{Binding MessageList}"></ListView>
<TextBlock Text="I'm such text to verify this control is showing" />
</Grid>
And the code behind is
public static readonly DependencyProperty MessageListProperty =
DependencyProperty.Register(
"MessageList", typeof(IEnumerable<string>), typeof(RecentList));
public IEnumerable<string> MessageList
{
get { return (IEnumerable<string>)GetValue(MessageListProperty); }
set { SetValue(MessageListProperty, value); }
}
The issue I have is the binding is not working. I can see this in the Output Window, with the Error:
Error 40 : BindingExpression path error: 'MessageList' property not found on 'object' ''MainWindowViewModel' (HashCode=26034861)'. BindingExpression:Path=MessageList; DataItem='MainWindowViewModel' (HashCode=26034861); target element is 'ListView' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
I understand the issue but I am confused by it. It's looking in the right place (in the MainWindowViewModel) but it is looking I don't understand why the UserControl is looking for the MessageList in the MainWindowViewModel. I guess it's because that is where I set the datacontext but, I also thought it that if I added this.DataContext = this; to the UserControl's constructor then it's wrong (I've tried it, it didn't work either).
Updating my UserControl to
<ListView ItemsSource="{Binding MessageList, RelativeSource={RelativeSource Mode=TemplatedParent}}"></ListView>
Helps in the sense I don't get the error message, but I also don't see the result.
This is what I think is happening when the application loads:
MainWindow loads
MainWindow then see's the UserControl and notes it requires a property.
Before WPF calls the UserControl constructor, it grabs the value of the property. It then initializes the component and automatically pushes the value to the UserControl's property
How can my UserControl use the Parents (MainWindow) property (Messages)
The Binding in the UserControl's XAML should have the UserControl instance as its source object, e.g. like this:
<ListView ItemsSource="{Binding MessageList,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
Alternatively you could set x:Name on the UserControl and use an ElementName binding:
<UserControl ... x:Name="self">
...
<ListView ItemsSource="{Binding MessageList, ElementName=self}" />
...
</UserControl>
Besides that you should usually not set the DataContext of the UserControl to itself (like DataContext = this;) because that would effectively prevent inheriting the DataContext from the UserControl's parent element, which is necessary for an "external" binding to work, like:
<uc:RecentList MessageList="{Binding Messages}" />

WPF binding textbox to dictionary entry

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]}"

DataGrid SelectedItem Being Bound to Wrong DataContext MVVM Pattern

I am trying to get the currently selected item of a datagrid that I have bound to a CollectionViewSource. However, it appears as if the SelectedItem property is not correctly binding to the property I have set in my ViewModel.
<Grid DataContext="{Binding CollectionView}">
<DataGrid ItemsSource="{Binding}" Margin="0,30,0,0" SelectedItem="{Binding SelectedRow}" />
</Grid>
When running the project, I see this error message in the output box of VS2010.
System.Windows.Data Error: 40 : BindingExpression path error: 'SelectedRow' property not found on 'object' ''BindingListCollectionView' (HashCode=56718381)'. BindingExpression:Path=SelectedRow; DataItem='BindingListCollectionView' (HashCode=56718381); target element is 'DataGrid' (Name=''); target property is 'SelectedItem' (type 'Object')
I understand that the SelectedItem property of the datagrid is trying to bind to the CollectionViewSource, but I am not quite sure how to tell the SelectedItem to bind to the SelectedRow property of my ViewModel. Any help would be appreciated. Also, if you need any more information on my setup, please feel free to ask.
Here is the property in my ViewModel, just in case it is needed:
public DataRow SelectedRow
{
get
{
return _selectedRow;
}
set
{
_selectedRow = value;
OnPropertyChanged("SelectedRow");
}
}
Change DataRow to whatever the actual type of object you are binding too is called.
public **Object each row represents in view model** SelectedRow
{
get
{
return _selectedRow;
}
set
{
_selectedRow = value;
OnPropertyChanged("SelectedRow");
}
}
I did some more digging, and was able to come up with a solution. Essentially, I needed to tell the SelectedItem property to look back at the DataContext of the MainWindow.
I changed the XAML to:
<Grid DataContext="{Binding CollectionView}">
<DataGrid ItemsSource="{Binding}" Margin="0,30,0,0" SelectedItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.SelectedRow}">
</DataGrid>
</Grid>
and then change the property within my ViewModel to a DataRowView instead of DataRow
public DataRowView SelectedRow
{
get
{
return _selectedRow;
}
set
{
_selectedRow = value;
OnPropertyChanged("SelectedRow");
}
}
Thanks everyone!
You have SelectedItem in your binding, and the name of your property is SelectedRow - make sure these are the same.
SelectedRow is not a property of CollectionView. I assume both are properties of your ViewModel:
<Grid DataContext="{Binding}">
<DataGrid ItemsSource="{Binding CollectionView}"
SelectedItem="{Binding SelectedRow}" />
</Grid>

Databinding - In XAML, how to databind to a property of the control created dynamically?

The control I created dynamically is a radiobutton, and I am trying to control the visibility of a hyperlinkbutton according to the IsChecked property of the radiobutton created in code-behind.
In my XAML file:
<HyperlinkButton Visibility="{Binding IsChecked, ElementName=tempRadio, Converter={StaticResource visibilityConvert}}" Content="Insert Record" Click="addRecord" Background="Aqua" Foreground="White"></HyperlinkButton>
Apparently I don't think I should use ElementName in this case, since it is only for controls created in XAML.
In my C# file:
public RadioButton tempRadio;
...
I would start with this:
first set the binding target on your hyperlink
hyperlinkButton.BindingTarget = tempRadio.IsChecked;
then set the binding:
hyperlinkButton.SetBinding(hyperlinkButton.BindingTarget, CreateValueBinding());
private Binding CreateValueBinding()
{
var valueBinding = new Binding();
valueBinding.Mode = BindingMode.TwoWay;
valueBinding.NotifyOnValidationError = true;
valueBinding.ValidatesOnExceptions = true;
valueBinding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
valueBinding.Path = new PropertyPath(this.DataMemberBinding.Path.Path);
return valueBinding;
}

Resources