Binding individual ListView items created in XAML - c#

I have a ListView with ListViewItem's created statically in XAML. I want to bind ListViewItem's properties to different properties in my View Model given as a DataContext for ListView. But my code does not work:
<ListView AlternationCount="2" Name="listView">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Binding.TargetUpdated="TargetUpdated" Text="{Binding Value}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Units}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding About}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
<l:PropertyItem Title="title" Value="{Binding Path=DeviceCode}" Units="units" About="about"/>
<l:PropertyItem Title="title" Value="{Binding Path=VendorName}" Units="units" About="about"/>
<l:PropertyItem Title="title" Value="{Binding Path=Version}" Units="units" About="about"/>
</ListView>
Where NumericItem is:
internal class PropertyItem : DependencyObject
{
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(String), typeof(PropertyItem), new PropertyMetadata(String.Empty));
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(String), typeof(PropertyItem), new PropertyMetadata(String.Empty));
public static readonly DependencyProperty UnitsProperty =
DependencyProperty.Register("Units", typeof(String), typeof(PropertyItem), new PropertyMetadata(String.Empty));
public static readonly DependencyProperty AboutProperty =
DependencyProperty.Register("About", typeof(String), typeof(PropertyItem), new PropertyMetadata(String.Empty));
public String Value
{
get
{
return (String)this.GetValue(ValueProperty);
}
set
{
this.SetValue(ValueProperty, value);
}
}
public String Title
{
get
{
return (String)this.GetValue(TitleProperty);
}
set
{
this.SetValue(TitleProperty, value);
}
}
public String Units
{
get
{
return (String)this.GetValue(UnitsProperty);
}
set
{
this.SetValue(UnitsProperty, value);
}
}
public String About
{
get
{
return (String)this.GetValue(AboutProperty);
}
set
{
this.SetValue(AboutProperty, value);
}
}
}

I found the solution! My PropertyItem class derived from DependencyObject which in turn does not contain property DataContext. That's why the binding does not work. You need to derive from the one who has DataContext property, for example FrameworkElement and it will work!

Try using these Bindings:
<l:PropertyItem Title="title" Value="{Binding Path=DataContext.DeviceCode,
RelativeSource={RelativeSource AncestorType={x:Type ListView}}}" Units="units"
About="about" />
<l:PropertyItem Title="title" Value="{Binding Path=DataContext.VendorName,
RelativeSource={RelativeSource AncestorType={x:Type ListView}}}" Units="units"
About="about"/>
<l:PropertyItem Title="title" Value="{Binding Path=DataContext.Version,
RelativeSource={RelativeSource AncestorType={x:Type ListView}}}" Units="units"
About="about"/>
Here, we're binding to the DataContext of the parent of type ListView... I believe that's what you were after.

Related

How to SelectItem of ListView having GridView

I want to select ListViewItem on Edit Button click of button, present in ListView inside GridView. Using MVVM Pattern.
Use case: Edit option in last column in GridViewColumn, on clicking edit button, I want to hide TextBlock and show TextBox, but to do this, I want to first selectItem of ListView on Edit Button's click
In below code,
I'm having ObservableCollection List,
User.cs is as below
private string _username;
private string _password;
private int _age;
private string _city;
public string Username { get => _username; set => _username = value; }
public string Password { get => _password; set => _password = value; }
public int Age { get => _age; set => _age = value; }
public string City { get => _city; set => _city = value; }
private bool _isEdit;
public bool IsEdit
{
get { return _isEdit; }
set
{
_isEdit = value;
OnPropertyChanged(nameof(IsEdit));
}
}
public User()
{
_username = String.Empty;
_password = String.Empty;
_city = String.Empty;
_isEdit = true;
}
public User(string username, string password, int age, string city)
{
this._username = username;
this._password = password;
this._age = age;
this._city = city;
_isEdit = true;
}
View File is as below
<ListView ItemsSource="{Binding List}"
Grid.Row="1"
SelectionMode="Single"
SelectedItem="{Binding SelectedUser}"
x:Uid="Table">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment"
Value="Center" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
<Setter Property="IsSelected"
Value="{Binding IsEdit, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumnHeader Style="{StaticResource HeaderStyle}">
<ContentControl ContentTemplate="{StaticResource Header}"
Content="Username" />
</GridViewColumnHeader>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Username}" />
<TextBox Text="{Binding Username}"
Visibility="{Binding IsEdit, Converter={StaticResource BoolToVisibilityConverter}}" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumnHeader Style="{StaticResource HeaderStyle}">Age</GridViewColumnHeader>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Age}" />
<TextBox Text="{Binding Age}"
Visibility="{Binding IsEdit, Converter={StaticResource BoolToVisibilityConverter}}" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumnHeader Style="{StaticResource HeaderStyle}">City</GridViewColumnHeader>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding City}" />
<TextBox Text="{Binding City}"
Visibility="{Binding IsEdit, Converter={StaticResource BoolToVisibilityConverter}}" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumnHeader Style="{StaticResource HeaderStyle}">Modify</GridViewColumnHeader>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="Edit"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=DataContext.Edit}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}, Path=ListViewItems}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
To select an item from code you need to set the Property that is bound to "SelectedItem" and then call a NotifyChange.
In your case it would be inside the Button Click-Event:
SelectedUser=yourItem;
NotifyChange(nameof(SelectedUser))

Binding Button command which inside ListView Item WPF MVVM

I have a ListView that outputs the content. total of 4 columns: Chapter number, title, edit button, delete button
When you click on the button, you need to get the element where it is located (for example, the Chapter number or Chapter name). I tried to do binding via the ListView name and via FindAncestor, but nothing happened.
Please help solve the problem or point out errors
XAML:
<ListView Name="TableOfContents"
ItemsSource="{Binding Path=ContentList}"
Background="{x:Null}" Width="600"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.CanContentScroll="True"
BorderBrush="{x:Null}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction
Command="{Binding Command}"
CommandParameter="{Binding ElementName=TableOfContents, Path=SelectedItem}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListView.View>
<GridView>
<GridViewColumn Width="50">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding ThemeID}" TextWrapping="Wrap"
Foreground="Black" FontSize="30"
TextAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="460">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding ThemeName}" TextWrapping="Wrap"
Foreground="Black" FontSize="20"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="40">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="🖊" ToolTip="Редактировать"
Foreground="Black" FontSize="18"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ItemsControl}},
Path=TableOfContentsPageViewModel.EditTheme}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="40">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="❌" ToolTip="Удалить"
Foreground="Black" FontSize="18"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ItemsControl}},
Path=TableOfContentsPageViewModel.DeleteTheme}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
ViewModel:
class TableOfContentsPageViewModel:BaseViewModel, IPageViewModel
{
public string SearchedChapter { get; set; }
public Visibility CanEdit { get; set; } = Visibility.Hidden;
public Theme SelectedTheme { get; set; }
public ObservableCollection<Theme> ContentList { get; set; }
public TableOfContentsPageViewModel()
{
ContentList = new ObservableCollection<Theme>(TrainSQL_Commands.GetAllThemes());
CanEdit = CurrentUser.Role == "Administrator" ? Visibility.Visible : Visibility.Hidden;
}
private ICommand _editTheme;
public ICommand EditTheme
{
get
{
return _editTheme ?? (_editTheme = new RelayCommand(x =>
{
MessageBox.Show("Edit theory");
}));
}
}
private ICommand _deleteTheory;
public ICommand DeleteTheme
{
get
{
return _deleteTheory ?? (_deleteTheory = new RelayCommand(x =>
{
MessageBox.Show("Delete theory");
}));
}
}
}
Image: https://i.stack.imgur.com/cpROA.png
Welcome to SO!
RelativeSource binds to the control itself, not via the DataContext (which make sense when you think about it). You're currently binding to the Path TableOfContentsPageViewModel.DeleteTheme, you need to change that to DataContext.TableOfContentsPageViewModel.DeleteTheme.

How to keep displaying selected Text in Combobox with Checkbox?

I'm making a color selector combobox with checkboxes.
Combobox's visible Text is "Colors", and items are the names of colors with a checkbox.
Scenario: open combobox -> select whatever in comboboxItems -> get checked(Selected)items.
So, when the user clicks a combobox item, I want to change the checkbox value and keep the combobox opened.
I'm stuck with the checking (selecting) items functionality.
How to do this?
Model:
public class ColorItem : DependencyObject
{
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register
("Name", typeof(string), typeof(ColorItem),
new PropertyMetadata(string.Empty));
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register
("IsSelected", typeof(bool), typeof(ColorItem),
new PropertyMetadata(true));
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
}
XAML:
<ComboBox Name="ColorCombobox" IsEditable="True" IsReadOnly="True" Focusable="False" StaysOpenOnEdit="True"
ItemsSource="{Binding ColorItems}" SelectionChanged="OnComboboxSelectionChanged" Text="Colors" Margin="0,0,30,0" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}" Width="20" />
<TextBlock Text="{Binding ColorName}" Width="100" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
SelectionChanged Event handler in code-behind:
private void OnComboboxSelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
ComboBox box = sender as ComboBox;
box.Text = "Colors"; //Not works. Text will empty be ""
}
Get rid of the StackPanel and the TextBlock and define an ItemContainerStyle that stretches the content of the ComboBoxItem:
<ComboBox Name="ColorCombobox" IsEditable="True" IsReadOnly="True" Focusable="False" StaysOpenOnEdit="True"
ItemsSource="{Binding ColorItems}" SelectionChanged="OnComboboxSelectionChanged" Text="Colors" Margin="0,0,30,0" >
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ComboBox.ItemContainerStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected}" Content="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

SelectedItem dependency property from IntelliBox does not update from Datatemplate

I'm using IntelliBox for showing autocomplete. The SelectedItem dependency property is implemented the following way:
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(object), typeof(Intellibox),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnSelectedItemChanged)));
This is how a new value is set in the usercontrol (IntelliBox)
private void ChooseCurrentItem() {
this.SetValue(SelectedItemProperty, ResultsList.SelectedItem);
_lastTextValue = UpdateSearchBoxText(true);
OnUserEndedSearchEvent();
if (Items != null) {
Items = null;
}
}
The property in the viewmodel
private Recipient selectedRecipient;
public Recipient SelectedRecipient
{
get
{
return selectedRecipient;
}
set
{
selectedRecipient = value;
OnPropertyChanged("SelectedRecipient");
}
}
If I use the IntelliBox without a datatemplate the setter of SelectedRecipient is called after setting value from dependency property.
<Grid>
<StackPanel>
<controls:Intellibox DataProvider="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},Path=DataContext.SearchProviderRecipients}"
SelectedItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},Path=DataContext.SelectedRecipient}">
</controls:Intellibox>
</StackPanel>
</Grid>
Unfortunately if the IntelliBox is within a datatemplate the value of the dependency property is set, but it does not call the setter in the viewmodel.
xaml:
<Grid>
<StackPanel>
<ListView ItemsSource="{Binding Items}" x:Name="ListViewMain">
<ListView.ItemTemplate>
<DataTemplate>
<controls:Intellibox DataProvider="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},Path=DataContext.SearchProviderRecipients}"
SelectedItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},Path=DataContext.SelectedRecipient}">
</controls:Intellibox>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Grid>
What have I have to do that the setter get called?

WPF LIstView Binding to Column Headers

I'm attempting to create a listview that binds dynamically to a set of dates. So the user would b able to select a date range and the results for the chosen dates would show up with the date in the column header. I've got it all working with just one problem, the dates don't show up in the header. I've got the following which I can't see any reason why it doesn't work:
public class KPIResult : DependencyObject
{
public static readonly DependencyProperty DateProperty = DependencyProperty.Register("Date", typeof(DateTime), typeof(KPIResult), new UIPropertyMetadata(null));
public DateTime Date
{
get { return (DateTime)GetValue(DateProperty); }
set { SetValue(DateProperty, value); }
}
public static readonly DependencyProperty ResultProperty = DependencyProperty.Register("Result", typeof(int), typeof(KPIResult), new UIPropertyMetadata(null));
public int Result
{
get { return (int)GetValue(ResultProperty); }
set { SetValue(ResultProperty, value); }
}
}
And the code for the ListView:
<ListView Margin="6" ItemsSource="{Binding ElementName=This, Path=KPICollection}" Name="lvKPIView" Grid.ColumnSpan="2">
<ListView.View>
<GridView>
<GridViewColumn Width="40" >
<GridViewColumnHeader Tag="KPIResult[0]" Content="{Binding Path=KPIResults[0].Date}" />
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=KPIResults[0].Result}" />
<TextBox Text="{Binding Path=KPIResults[0].Result}" />
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
The results are displayed just fine. Just no dates in headers :(.
Any ideas guys?
Cheers,
SumGuy
Try this way:
<ListView Margin="6" DataContext="{Binding ElementName=This}" ItemsSource="{Binding KPICollection}" Name="lvKPIView" Grid.ColumnSpan="2">
<ListView.View>
<GridView>
<GridViewColumn Width="40" >
<GridViewColumnHeader Tag="KPIResult[0]" Content="{Binding KPICollection.KPIResults[0].Date}" />
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=KPIResults[0].Result}" />
<TextBox Text="{Binding Path=KPIResults[0].Result}" />
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Apparently, the problem is that you are trying to bind to a property without specifying binding source and without having DataContext of the control set to anything. The reason why binding inside CellTemplate work is that the data context for rows is automatically set to the corresponding list item instance, but this is not true for headers - they inherit data context from the parent control. So, if we specify DataContext for the ListView then binding that is used in the header will be have a relative path to that data context: {Binding KPICollection.KPIResults[0].Date}

Categories

Resources