I have a ComboBox in a UWP project. I am binding the DataSource to a collection of MyItem class. My class looks like this:
public class MyItem : INotifyPropertyChanged
{
#region INPC
public event PropertyChangedEventHandler PropertyChanged;
protected void Notify(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
private string _ItemName;
public string ItemName
{
get { return _ItemName; }
set
{
if (value != _ItemName)
{
_ItemName = value;
Notify("ItemName");
}
}
}
private bool _ItemEnabled;
public bool ItemEnabled
{
get { return _ItemEnabled; }
set
{
if (value != _ItemEnabled)
{
_ItemEnabled = value;
Notify("ItemEnabled");
}
}
}}
Now I want the ComboBoxItem to be enabled or disabled depending on my ItemEnabled property. I researched and tried adding a binding through the Style tag but the binding does not function in UWP.
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="IsEnabled" Value="{Binding ItemEnabled}" />
</Style>
</ComboBox.ItemContainerStyle>
How can I solve this?
EDIT 1: Binding code
<ComboBox ItemsSource="{Binding Path=MyItemsCollection, UpdateSourceTrigger=PropertyChanged}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ItemName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Just remove this line (ItemsSource="{Binding Path=MyItemsCollection, UpdateSourceTrigger=PropertyChanged}") in XAML and add this line after InitializeComponent(); in the code behind:
<ComboBox Name="cmb1">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ItemName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="IsEnabled" Value="{Binding ItemEnabled}" />
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
And in xaml.cs:
public Window1()
{
InitializeComponent();
cmb1.ItemsSource = MyItemsCollection;
}
Edit: Another way is like this:
public Window1()
{
InitializeComponent();
this.DataContext = MyItemsCollection;
}
And in the xaml:
<ComboBox Name="cmb1" ItemsSource="{Binding}">
....
Related
how can I expand my bound item in treeview?
My item's class:
public class TreeViewItemBase : INotifyPropertyChanged
{
private bool isSelected;
public bool IsSelected
{
get { return this.isSelected; }
set
{
if (value != this.isSelected)
{
this.isSelected = value;
NotifyPropertyChanged("IsSelected");
}
}
}
private bool isExpanded;
public bool IsExpanded
{
get { return this.isExpanded; }
set
{
if (value != this.isExpanded)
{
this.isExpanded = value;
NotifyPropertyChanged("IsExpanded");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
public class FileModelViewTreeView : TreeViewItemBase
{
public int Level
{
get;
set;
}
public string Name
{
get;
set;
}
public string Path
{
get;
set;
}
public List<FileModelViewTreeView> SubItems
{
get;
set;
}
}
This code doesn't works by me:
((FileModelViewTreeView)toSelectIndex).IsExpanded = true;
((FileModelViewTreeView)toSelectIndex).IsSelected = true;
My treeview xaml code:
<TreeView SelectedItemChanged="FilesTreeView_SelectedItemChanged" x:Name="FilesTreeView" Margin="0,68,0,0" HorizontalAlignment="Left" Width="200" Background="Transparent" BorderBrush="#4c607a" BorderThickness="2">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:FileModelViewTreeView}" ItemsSource="{Binding SubItems}">
<StackPanel Orientation="Horizontal" Margin="2">
<Image Source="pack://application:,,,/UI/Images/dir.png"
Width="21"
Height="21"
SnapsToDevicePixels="True"/>
<TextBlock Padding="5" Text="{Binding Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
All solutions which I found on other forums didn't worked for me :(
There are no exception at runtime and compiling, it just doesn't works. Maybe anyone already had that problem, please help me.
The cleanest way to do this is using a style.
<Style x:Key="MyTreeViewItemContainerStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
...
</style>
<Style TargetType="{x:Type TreeView}">
<Setter Property="ItemContainerStyle" Value="{StaticResource MyTreeViewItemContainerStyle}" />
</Style>
Check out my blog post for my take on how to handle WPF TreeViews in a MVVM context.
I am trying to bind a list of numbers to a listbox and set a doubleClickCommand on so that when I double click on an Item it will run the SetItem Method.
My View
<Grid>
<StackPanel>
<TextBlock Text="{Binding Item}"/>
<ListBox ItemsSource="{Binding List}" Height="515" SelectedItem="{Binding SelectedItem}" Grid.Column="0">
<ListBoxItem>
<ListBoxItem.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding Command}" />
</ListBoxItem.InputBindings>
</ListBoxItem>
</ListBox>
</StackPanel>
</Grid>
And my ViewModel
public class MainWindowViewModel : BindableBase
{
public DelegateCommand Command { get; private set; }
private string _title = "Prism Application";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public MainWindowViewModel()
{
Command = new DelegateCommand(SetItem);
List = new List<string>();
List.Add("one");
List.Add("two");
List.Add("three");
}
private void SetItem()
{
Item = SelectedItem;
}
private string _item;
public string Item
{
get { return _item; }
set { SetProperty(ref _item, value); }
}
private string _selectedItem;
public string SelectedItem
{
get { return _selectedItem; }
set { SetProperty(ref _selectedItem, value); }
}
private List<string> _list;
public List<string> List
{
get { return _list; }
set { SetProperty(ref _list, value); }
}
}
When I try to run the code I get this exception.
InvalidOperationException: Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead
Is there a way to fix this; or do I need to do this some other way?
The exception means that you cannot both add a ListBoxItem with an InputBinding to the ListBox and bind the ItemsSource at the same time.
There are multiple ways to invoke a command when a ListBoxItem is clicked. One of them is to add an InputBinding to an element in the ItemTemplate, e.g.:
<ListBox ItemsSource="{Binding List}" Height="515" SelectedItem="{Binding SelectedItem}" Grid.Column="0">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<Grid.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick"
Command="{Binding DataContext.Command,
RelativeSource={RelativeSource AncestorType=ListBox}}" />
</Grid.InputBindings>
<TextBlock Padding="4,1" Text="{Binding}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Padding" Value="0" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
I am trying to bind columns and rows of DataGrid to the same source but DataGrid does not show new value in the ColumnHeader when I change value in the RowHeader after TextBox lost focus. Headers collection contains new values as expected.
xmlns:dataGrid2D="http://gu.se/DataGrid2D"
<Grid>
<DataGrid
dataGrid2D:ItemsSource.RowHeadersSource="{Binding Headers}"
dataGrid2D:ItemsSource.ColumnHeadersSource="{Binding Headers}"
dataGrid2D:ItemsSource.Array2D="{Binding Items}"
IsReadOnly="True"
AutoGenerateColumns="True">
<DataGrid.RowHeaderTemplate>
<DataTemplate>
<TextBox Text="{Binding Value}" />
</DataTemplate>
</DataGrid.RowHeaderTemplate>
</DataGrid>
</Grid>
DataContext:
public class MainViewModel
{
public IEnumerable<Header> Headers { get; } = new Header[]
{ new Header { Value = "1" }, new Header { Value = "2" } };
public string[,] Items { get; } = new string[2, 2] { { "1", "2" }, { "3", "4" } };
}
public class Header
{
public string Value { get; set; }
public override string ToString()
{
return Value;
}
}
What I am doing wrong?
I am changing the Value property from the View side so it does not need INotifyPropertyChanged interface. If I call "Refresh" on DataGrid it updates but with blinking.
Edit:
Why are you downvoting me? Is it a bad question?
Here is one possible solution using the style for DataGridColumnHeader. I also explicitly used the Value property instead of the ToString method.
<Grid>
<DataGrid
dataGrid2D:ItemsSource.RowHeadersSource="{Binding Headers}"
dataGrid2D:ItemsSource.ColumnHeadersSource="{Binding Headers}"
dataGrid2D:ItemsSource.Array2D="{Binding Items}"
IsReadOnly="True"
AutoGenerateColumns="True">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding Value}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>
<DataGrid.RowHeaderTemplate>
<DataTemplate>
<TextBox Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGrid.RowHeaderTemplate>
</DataGrid>
</Grid>
You need to slightly change the ModelView then:
public class Header: INotifyPropertyChanged
{
private string _Value;
public string Value
{
get
{
return _Value;
}
set
{
_Value = value;
OnPropertyChanged("Value");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
I'm trying to display different rows in a datagrid with different cellcontent sometimes.
I have different classes for the different rows, for example
Class 1:
Name - Description - Checkbox
Class 2:
Name - Description - Textbox(user input at runtime) - Checkbox
Class 3
Name - Textbox(user input at runtime)
The classes are related by inheritance so I can use them within the same observablecollection.
I want to display these in a datagrid based on which class I chose to add, for example:
ObservableCollection<Rowitem> rowitems = new ObservableCollection<Rowitem>();
rowitems.Add(new Class1("Tom", "Nice", false));
rowitems.Add(new Class2("John", "Strange", Empty textbox , true));
rowitems.Add(new Class3("Roger", Empty Textbox));
.. meaning I would like the datagrid to display an empty textbox in the third column on the second row where there is a checkbox in the first row and nothing in the third row. Is this possible?
Here is my suggestion:
<Window x:Class="DataGridDynamicCellView.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DataGridDynamicCellView"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="525"
Height="350"
mc:Ignorable="d">
<Window.DataContext>
<local:DynamicCellsDataContext />
</Window.DataContext>
<Grid>
<DataGrid ItemsSource="{Binding DataGridSource}">
<DataGrid.Resources>
<DataTemplate DataType="{x:Type local:PresentedByCheckBox}">
<Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<CheckBox HorizontalAlignment="Center"
VerticalAlignment="Center"
IsChecked="{Binding IsChecked,
UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type local:PresentedByTextBox}">
<Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding HelloWorld,
UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type local:PresentedByComplexBox}">
<StackPanel HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Orientation="Horizontal">
<Ellipse Height="10" Width="10" Fill="Pink"/>
<CheckBox HorizontalAlignment="Center"
VerticalAlignment="Center"
IsChecked="{Binding Checked,
UpdateSourceTrigger=PropertyChanged}" />
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding HelloWorld,
UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</DataTemplate>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="BorderBrush" Value="Green" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<ContentControl Content="{Binding}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>
</DataGrid>
</Grid></Window>
MVVM view model:
public class DynamicCellsDataContext:BaseObservableObject
{
public DynamicCellsDataContext()
{
DataGridSource = new ObservableCollection<object>
{
new PresentedByTextBox("Hello world!!!"),
new PresentedByCheckBox(true),
new PresentedByComplexBox("Hello world!!!", true),
};
}
public ObservableCollection<object> DataGridSource { get; set; }
}
public class PresentedByComplexBox:BaseObservableObject
{
private string _helloWorld;
private bool _checked;
public string HelloWorld
{
get { return _helloWorld; }
set
{
_helloWorld = value;
OnPropertyChanged();
}
}
public bool Checked
{
get { return _checked; }
set
{
_checked = value;
OnPropertyChanged();
}
}
public PresentedByComplexBox(string helloWorld, bool isChecked)
{
HelloWorld = helloWorld;
Checked = isChecked;
}
}
public class PresentedByCheckBox:BaseObservableObject
{
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
OnPropertyChanged();
}
}
public PresentedByCheckBox(bool isChecked)
{
IsChecked = isChecked;
}
}
public class PresentedByTextBox:BaseObservableObject
{
private string _helloWorld;
public string HelloWorld
{
get { return _helloWorld; }
set
{
_helloWorld = value;
OnPropertyChanged();
}
}
public PresentedByTextBox(string helloWorld)
{
HelloWorld = helloWorld;
}
}
The BaseObservableObject class:
public class BaseObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> raiser)
{
var propName = ((MemberExpression)raiser.Body).Member.Name;
OnPropertyChanged(propName);
}
protected bool Set<T>(ref T field, T value, [CallerMemberName] string name = null)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
OnPropertyChanged(name);
return true;
}
return false;
}
}
That's all, let me know in case you will need more examples.
Best regards.
You can do this using DataTemplates:
Just add them to the Resources of your Grid:
<Grid.Resources>
<DataTemplate DataType="{x:Type local:Class1}">
<-- Template for class1 -->
</DataTemplate>
<DataTemplate DataType="{x:Type local:Class2}">
<-- Template for class2 -->
</DataTemplate>
</Grid.Resources>
I want to display several names, and I want them to be editable.
So I used an ObservableColection, and bind it to a ListView with the new x:Bind feature.
Here's my XAML:
<ListView>
<ListView ItemsSource="{x:Bind ViewModel.Players}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate xmlns:model="using:Flechette.Model" x:DataType="model:Player">
<TextBox Text="{x:Bind Name, Mode=TwoWay}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And my code behind:
public sealed partial class GameSettingsPage : Page
{
ViewModel.GameSettingsViewModel ViewModel { get; set; }
public GameSettingsPage()
{
InitializeComponent();
DataContextChanged += (s, e) => ViewModel = DataContext as ViewModel.GameSettingsViewModel;
}
}
The problem is that TwoWay binding failed to compile, I get the error CS1061 'WeakReference' does not contain a definition for 'LostFocus' and no extension method 'LostFocus' accepting a first argument of type 'WeakReference' could be found (are you missing a using directive or an assembly reference?)
How can I fix it ?
This seems like a problem in the preview version of Windows 10 SDK. Given the following code:
MainPage.xaml:
<ListView x:Name="Players">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Player">
<TextBox Text="{x:Bind Name, Mode=TwoWay}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
MainPage.xaml.cs:
private ObservableCollection<Player> players = new ObservableCollection<Player>();
public MainPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this.players.Add(new Player());
this.players.Add(new Player());
this.players.Add(new Player());
this.players.Add(new Player());
this.players.Add(new Player());
this.Players.ItemsSource = players;
}
Player.cs:
public class Player : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set
{
if (value == name) return;
name = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The compilation and app works and provides the expected behavior: