I have the following functioning code that binds GridViewColumns to data from a custom class:
<ListView Name="lv">
<ListView.View>
<GridView>
<GridViewColumn Header="First" DisplayMemberBinding="{Binding lvi.firstName}"/>
<GridViewColumn Header="Last" DisplayMemberBinding="{Binding lvi.lastName}"/>
</GridView>
</ListView.View>
</ListView>
public class LVItemBox {
public LVItem lvi { get; set; }
}
public class LVItem : INotifyPropertyChanged {
private string _firstName;
private string _lastName;
public string firstName {
get { return _firstName; }
set { SetField(ref _firstName, value); }
}
public string lastName {
get { return _lastName; }
set { SetField(ref _lastName, value); }
}
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged(string propertyName) {PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null) { if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
LVItem lvi1 = new LVItem {firstName = "John", lastName = "Doe"};
LVItem lvi2 = new LVItem {firstName = "Jane", lastName = "Smith"};
lv.Items.Add(new LVItemBox {lvi = lvi1});
lv.Items.Add(new LVItemBox {lvi = lvi2});
}
}
My dilemma is that I want background / foreground Brush capability within LVItemBox, however if I make LVItemBox extend Control, changing Background/Foreground has no effect:
public class LVItemBox : Control {
public LVItem lvi { get; set; } // data displays
}
...
...
private void changeBackground(object sender, EventArgs e) {
LVItemBox lvib = (LVItemBox)lv.Items[0];
lvib.Background = Brushes.Black; // doesn't work
}
Furthermore, if I extend ListViewItem instead of Control I can get the background change to work, but the data bindings no longer work.
public class LVItemBox : ListViewItem {
public LVItem lvi { get; set; } // data doesn't display
}
...
...
private void changeBackground(object sender, EventArgs e) {
LVItemBox lvib = (LVItemBox)lv.Items[0];
lvib.Background = Brushes.Black; // works
}
Any idea how I can get foreground / background capability within LVItemBox?
Inheriting from Control works if you add the following ItemContainerStyle to your XAML:
<ListView Name="lv">
<ListView.View>
<GridView>
<GridViewColumn Header="First" DisplayMemberBinding="{Binding lvi.firstName}"/>
<GridViewColumn Header="Last" DisplayMemberBinding="{Binding lvi.lastName}"/>
</GridView>
</ListView.View>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Background" Value="{Binding Background}" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
Related
This question already has answers here:
Why does WPF support binding to properties of an object, but not fields?
(2 answers)
Closed last year.
This is the code behind, an array of People (ObservableCollection ) objects.
private ObservableCollection<People> data = new ObservableCollection<People>();
public SecondWindow()
{
InitializeComponent();
data.Add(new People() { Name = "JOhn Doe", Age="34" });
data.Add(new People() { Name = "Jane Doe", Age = "45" });
data.Add(new People() { Name = "Peter Singh", Age = "26" });
this.DataContext = this;
}
public class People : INotifyPropertyChanged
{
private String name, age;
public String Name
{
get { return this.name; }
set
{
if (this.name != value)
{
this.name = value;
this.NotifyPropertyChanged("Name");
}
}
}
public String Age
{
get { return this.age; }
set
{
if (this.age != value)
{
this.age = value;
this.NotifyPropertyChanged("Age");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String prop)
{
if (this.PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
This is the XAML code, but it's not binding
<BlockUIContainer>
<ListView BorderThickness="0"
ItemsSource="{Binding Path=data}">
<ListView.View>
<GridView>
<GridViewColumn
Header="Name"
DisplayMemberBinding="{Binding Name}"
Width="150" />
<GridViewColumn
Header="Age"
DisplayMemberBinding="{Binding Age}"
Width="75" />
</GridView>
</ListView.View>
</ListView>
</BlockUIContainer>
Any ideas?
The problem comes from using a field and not a public property for data:
public ObservableCollection<People> data { get; } = new ObservableCollection<People>();
i'm quite the rookie in the WPF enviroment
i have been scouring for a solution, although i'm sure it's just something very basic i have yet to understand
I'm trying to make use of Observable collection to update a Listview
I have added a method in the viewmodel, i need to call from outside code to add another item to the list.
When i call method addTask in the ViewModel with debugger on, i can see it counts up 1 item in the list. But it doesn't add it to the ListView
Model:
public class Tasks : INotifyPropertyChanged
{
private string taskName;
private string fromTime;
private string toTime;
private string message;
private string taskCreator;
public string TaskName
{
get
{
return taskName;
}
set
{
taskName = value;
OnPropertyChanged("TaskName");
}
}
public string FromTime
{
get
{
return fromTime;
}
set
{
fromTime = value;
OnPropertyChanged("FromTime");
}
}
public string ToTime
{
get
{
return toTime;
}
set
{
toTime = value;
OnPropertyChanged("ToTime");
}
}
public string Message
{
get
{
return message;
}
set
{
message = value;
OnPropertyChanged("Message");
}
}
public string TaskCreator
{
get
{
return taskCreator;
}
set
{
taskCreator = value;
OnPropertyChanged("TaskCreator");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
the ViewModel:
class TasksViewModel
{
public TasksViewModel()
{
{
_UsersList.Add(new Tasks() { TaskName = "TaskName1", FromTime = "03:00", ToTime = "07:00", TaskCreator = "TaskCreator1", Message = "Hello" });
_UsersList.Add(new Tasks() { TaskName = "TaskName2", FromTime = "03:00", ToTime = "07:00", TaskCreator = "TaskCreator2", Message = "Hello" });
_UsersList.Add(new Tasks() { TaskName = "TaskName3", FromTime = "03:00", ToTime = "07:00", TaskCreator = "TaskCreator3", Message = "Hello" });
};
}
public ObservableCollection<Tasks> Tasks
{
get { return _UsersList; }
}
public ObservableCollection<Tasks> _UsersList { get; set; } = new ObservableCollection<Tasks>();
public void addTask(string taskName, string fromTime, string toTime, string taskCreator, string message)
{
_UsersList.Add(new Tasks() { TaskName = taskName, FromTime = fromTime, ToTime = toTime, TaskCreator = taskCreator, Message = message });
}
The list view i want to update (Xaml)
<ListView Name="TaskGrid1" Grid.Row="1" Grid.Column="1" Margin="4,4,12,13" ItemsSource="{Binding Tasks}" RenderTransformOrigin="0.5,0.5" FontSize="30" >
<ListView.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="0"/>
<TranslateTransform/>
</TransformGroup>
</ListView.RenderTransform>
<ListView.View>
<GridView x:Name="List00000600">
<GridViewColumn Header="Tid" DisplayMemberBinding="{Binding FromTime}" Width="100"/>
<GridViewColumn Header="Opgave" DisplayMemberBinding="{Binding TaskName}" Width="350" />
<GridViewColumn Header="Opretter" DisplayMemberBinding="{Binding TaskCreator}" Width="120" />
</GridView>
</ListView.View>
</ListView>
I have no idea how you've assigned viewmodel in app.xaml.
Just open the xaml file, which holds your listview and build your window as usual:
<Window
... (rest of xmlns)
xmlns:MyViewModels="clr-namespace:YourViewModelNamespace"
>
<Window.DataContext>
<MyViewModels:TasksViewModel/>
</Window.DataContext>
<Grid/Or any container>
...
<ListView... />
</Grid/Or any container
</Window>
As mentioned, replace _UserList with Tasks.
Your async TasksCreate() is creating new instance of TasksViewModel so it will never update current one.
PS: you can obtain viewmodel by:
// this function belongs to mainwindow/anywindow
public void CodeBehindClickEvent(object sender, RoutedEventArgs e)
{
var VM = (TasksViewModel)this.DataContext;
VM.addTask("blabla", ...)
VM.TasksCreate();
}
new to WPF here. This first app I'm building is using a RelayCommand and DataGrid with ButtonCommand to edit or enter a new user. For a new user with values entered into the open row, the OneditButtonCommand is being called when pressed, but the Item being passed as a parameter is always null. I tried switching the order of Command and CommandParameter in the XAML to see if the Command was being set before a defined parameter, but the button command object was still NULL. Does any solution jump out to anyone? Many thanks in advance!
The ViewModel:
public partial class UsersViewModel : INotifyPropertyChanged
{
public RelayCommand<UserViewModel> editButton_Click_Command { get; set; }
public UsersViewModel()
{
editButton_Click_Command = new RelayCommand<UserViewModel>(OneditButton_Click_Command);
this.Users = new ObservableCollection<UserViewModel>();
this.Users.Add(new UserViewModel() { FirstName = "John", LastName = "Doe", EMail = "JohnDoe#yahoo.com", EndDate = new DateTime(2016,2,1), Position = "Developer", UserID = 0 });
}
private ObservableCollection<UserViewModel> _Users;
public ObservableCollection<UserViewModel> Users
{
get { return _Users; }
set { _Users = value; NotifyPropertyChanged("Users"); }
}
private void OneditButton_Click_Command(UserViewModel obj)
{
//Parameter object is always NULL here!!!
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
The XAML:
<Window x:Name="Base_V"
x:Class="DbEntities.UsersWindow"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DbEntities"
xmlns:ViewModels="clr-namespace:DbEntities"
xmlns:staticData="clr-namespace:DbEntities"
mc:Ignorable="d"
Title="UsersWindow" Height="Auto" Width="900">
<Window.Resources>
<staticData:PositionsList x:Key="PositionsList" />
</Window.Resources>
<Window.DataContext>
<ViewModels:UsersViewModel/>
</Window.DataContext>
<Grid>
<FrameworkElement x:Name="dummyElement" Visibility="Collapsed" />
<DataGrid Name="DataGrid1" ItemsSource="{Binding Users}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
ColumnWidth="*" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Command="{Binding DataContext.editButton_Click_Command, ElementName=Base_V}" CommandParameter="{Binding}" >Edit</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="User ID" Binding="{Binding UserID}" IsReadOnly="True" />
<DataGridTextColumn Header="Last Name" Binding="{Binding LastName}" />
<DataGridTextColumn Header="First Name" Binding="{Binding FirstName}" />
<DataGridTextColumn Header="E-Mail" Binding="{Binding EMail}" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<Label Content="Position" />
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{StaticResource PositionsList}" SelectedItem="{Binding Position}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="End Date" Binding="{Binding EndDate, StringFormat={}{0:MM/dd/yyyy}}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
The RelayCommand was taken from here: http://www.kellydun.com/wpf-relaycommand-with-parameter/
EDIT:
It was suggested that the UserViewModel be posted to see if the issue can be resolved here or in another question. There are a couple of test users pulled from a database as well.
UserViewModel:
public class UserViewModel : INotifyPropertyChanged
{
private string _FirstName;
public string FirstName
{
get { return _FirstName; }
set { _FirstName = value; NotifyPropertyChanged("FirstName"); }
}
private string _LastName;
public string LastName
{
get { return _LastName; }
set { _LastName = value; NotifyPropertyChanged("LastName"); }
}
private string _EMail;
public string EMail
{
get { return _EMail; }
set { _EMail = value; NotifyPropertyChanged("EMail"); }
}
private int _UserID;
public int UserID
{
get { return _UserID; }
set { _UserID = value; NotifyPropertyChanged("UserID"); }
}
private string _Position;
public string Position
{
get { return _Position; }
set { _Position = value; NotifyPropertyChanged("Position"); }
}
private DateTime? _EndDate;
public DateTime? EndDate
{
get { return _EndDate; }
set { _EndDate = value; NotifyPropertyChanged("EndDate"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
On the home page:
UsersViewModel Usersvm = new UsersViewModel();
Usersvm.Users = new ObservableCollection<UserViewModel>();
DbEntities db = new DbEntities();
var pulledUsers = db.uspGetNonAdminUsers().ToList();
foreach (var result in pulledUsers)
{
var pulledUser = new UserViewModel
{
FirstName = result.FirstName,
LastName = result.LastName,
EMail = result.Email,
UserID = result.UserID,
Position = result.Position,
EndDate = result.EndDate
};
Usersvm.Users.Add(pulledUser);
}
new UsersWindow(Usersvm).Show();
The UsersWindow:
public partial class UsersWindow : Window
{
public UsersWindow(UsersViewModel uvm)
{
InitializeComponent();
DataContext = uvm;
}
}
You need to do a few things:
Update your property to be type object:
public RelayCommand<object> editButton_Click_Command { get; set; }
update your instantiation to use object
editButton_Click_Command = new RelayCommand<object>(OneditButton_Click_Command);
then, update your event handler to have a param object and cast accordingly.
private void OneditButton_Click_Command(object obj)
{
var associatedViewModel = obj as UserViewModel;
if (associatedViewModel != null)
{
}
}
I've created a class that I need to have Visibility property like other UI controls. It looks like this:
More extended code:
xaml:
<ListBox x:Name="itemsHolder" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Surname}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code behind:
public ObservableCollection<MyClass > myVM= new ObservableCollection<MyClass>();
public class MyClass : Control //FrameworkElement
{
public string Name { get; set; }
public string Surname { get; set; }
}
...
MyClass my1 = new MyClass();
my1.Name = "Test";
my1.Surname = "Test2";
myVM.Add(my1);
itemsHolder.ItemsSource = myVm;
...
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
foreach(MyClass item in itemsHolder.Items)
{
if(!item.Name.Contains((sender as TextBox).Text))
{
item.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
else
{
item.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
}
}
What I try to do is something like a filter(search) and I want to hide items. Just adding a property to the class also does not work.
You are very close... to get this working your class MyClass must implement INotifyPropertyChanged, I use the base class bindable which makes implementing INotifyPropertyChanged much simpler.
Below is the answer
xaml:
<ListBox Grid.Row="1" x:Name="itemsHolder" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Visibility="{Binding IsVisible}">
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Surname}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
code:
public ObservableCollection<MyClass > myVM= new ObservableCollection<MyClass>();
public class Bindable:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MyClass : Bindable {
private string _Name;
public string Name {
get { return _Name; }
set
{
if (_Name != value)
{
_Name = value;
OnPropertyChanged();
}
}
}
private string _Surname;
public string Surname
{
get { return _Surname; }
set{
if (_Surname != value)
{
_Surname = value;
OnPropertyChanged();
}
}
}
public Visibility _IsVisible;
public Visibility IsVisible {
get { return _IsVisible; }
set {
if (_IsVisible != value)
{
_IsVisible = value;
OnPropertyChanged();
}
}
}
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
foreach(MyClass item in itemsHolder.Items)
{
if(!item.Name.Contains((sender as TextBox).Text))
{
item.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
else
{
item.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
}
}
I have ListView with GridView inside view of ListView and ListView item source is specified. I dont seem to find how can. I get SelectedItem of GridView or SelectedItem changed.
<ListView Grid.Row="4" Margin="0,250,0,0" ItemsSource="{Binding TestBinding}" SelectedItem="{Binding Path=selectedItem}" IsSynchronizedWithCurrentItem="True" HorizontalAlignment="Left" SelectionChanged="ListView_SelectionChanged">
<ListView.View>
<GridView AllowsColumnReorder="False" >
<GridViewColumn Header="Test" DisplayMemberBinding="{Binding Path=Test1}" Width="100" />
<GridViewColumn Header="Test2" DisplayMemberBinding="{Binding Path=Test2}" Width="130" />
</GridView>
</ListView.View>
</ListView>
Here is my code and it works fine:
public partial class MainWindow : Window, INotifyPropertyChanged, INotifyPropertyChanging
{
public class MyObj
{
public string Test1 { get; set; }
public string Test2 { get; set; }
}
public MainWindow()
{
InitializeComponent();
TestBinding = new ObservableCollection<MyObj>();
for (int i = 0; i < 5; i++)
{
TestBinding.Add(new MyObj() { Test1 = "sdasads", Test2 = "sdsasa" });
}
DataContext = this;
}
#region TestBinding
private ObservableCollection<MyObj> _testBinding;
public ObservableCollection<MyObj> TestBinding
{
get
{
return _testBinding;
}
set
{
if (_testBinding != value)
{
NotifyPropertyChanging("TestBinding");
_testBinding = value;
NotifyPropertyChanged("TestBinding");
}
}
}
#endregion
#region selectedItem
private MyObj _selectedItem;
public MyObj selectedItem
{
get
{
return _selectedItem;
}
set
{
if (_selectedItem != value)
{
NotifyPropertyChanging("selectedItem");
_selectedItem = value;
NotifyPropertyChanged("selectedItem");
}
}
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
// Used to notify the page that a data context property changed
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region INotifyPropertyChanging Members
public event PropertyChangingEventHandler PropertyChanging;
// Used to notify the data context that a data context property is about to change
protected void NotifyPropertyChanging(string propertyName)
{
if (PropertyChanging != null)
{
PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
}
}
#endregion
}