WPF - Observable collection binding error - c#

I am tring to create a simple program that connect ObservableCollection to ListBox. I wrote:
public ObservableCollection<int> Values { get; set; }
and
public MainWindow()
{
InitializeComponent();
Values = new ObservableCollection<int>();
Values.Add(1);
DataContext = this;
}
then I was created button and wrote:
public Button1_Clicke(object sender, RoutedEventArgs e)
{
Values.Add(2);
}
XMAL:
<ListBox x:Name="list" ItemsSource="{Binding Path=Values}"/>
When the window opened I able to see the '1' value.
But when I clicked the button, The list box dosent update the items. What is wrong?

You can try this:
<ListBox x:Name="list" ItemsSource="{Binding Path=Values}"/>
EDIT:
I have made a simple sample as below:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel>
<ListBox x:Name="list" ItemsSource="{Binding Path=Values}"/>
<Button Click="Button_Click" Content="Test"></Button>
</StackPanel>
</Window>
Code behind (Window1.xaml.cs)
using System.Collections.ObjectModel;
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public ObservableCollection<int> Values { get; set; }
public Window1()
{
InitializeComponent();
Values = new ObservableCollection<int>();
Values.Add(1);
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Values.Add(2);
}
}
It is working as expected. So base on your comments below why don't you try remove all of converter to make sure it correct or not.

Related

Element added to ObservableCollection in ViewModel not displayed

Quite a newbie in the world of WPF. I've been hurting my eyes for several days now, looking for a solution, but didn't manage to make it right.
What I'm trying to achieve :
Create an ObservableCollection in a view model class
Bind the data inside the .xaml file
Create a "Add" button from private void Button_Click in the MainWindow.xaml.cs (I'll create commands later - I just need to take it step by step and keep it simple to understand the process.)
Seems quite simple, uh ? I manage to do it all in the MainWindow.xaml.cs (only using ObservableCollection, no 'PropertyChanged' yet), but I can't make it work in the ViewModel.
Now my code :
 
Client.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DataBindingTestInViewModel
{
class Client
{
protected string _firstName;
protected string _name;
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
public string Name
{
get { return _name; }
set { _name = value; }
}
}
}
MainWindow.xaml
<Window x:Class="DataBindingTestInViewModel.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DataBindingTestInViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ItemsControl Grid.Row="0" ItemsSource="{Binding Clients}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Label Grid.Column="0" Content="{Binding FirstName}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Grid.Row="1" Click="Button_Click">Add a client</Button>
</Grid>
</Window>
MainWindow.xaml.cs (create an instance of ViewModel + Button_Click event)
using System.Windows;
namespace DataBindingTestInViewModel
{
/// <summary>
/// Logique d'interaction pour MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
ViewModel viewmodel = new ViewModel();
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
viewmodel.AddClient();
}
}
}
ViewModel.cs
using System.Collections.ObjectModel;
using System.Windows;
namespace DataBindingTestInViewModel
{
class ViewModel
{
private ObservableCollection<Client> _clients = new ObservableCollection<Client>();
public ViewModel()
{
_clients.Add(new Client() { FirstName = "John", Name = "Doe" });
_clients.Add(new Client() { FirstName = "Jane", Name = "Doe" });
//MessageBox.Show("Constructeur du View Model appelé.");
}
public ObservableCollection<Client> Clients
{
get { return _clients; }
}
public void AddClient()
{
_clients.Add( new Client() { FirstName = "Donald", Name = "Trump" } );
MessageBox.Show("First element : " + _clients[0].FirstName + "\n" +
"Third element : " + _clients[2].FirstName );
}
}
}
Results :
The "John Doe/Jane Doe" are displayed
When I click the button it doesn't add the thing in the UI. (But the Messagebox works fine.)
What I think happens :
The MainWindow data context is binded to the ViewModel's Client instance. I create and add element in a new instance called in MainWindow - and that's not the one used in the datacontext.
Questions :
Am I right ? If so, how to solve it (call the Viewmodel's instance's AddClient method in the MainWindow) ?
Any other better (but simple, please !) methods ?
I guess that'll be a piece of cake for y'all. Thank you !!
you are correct about working with 2 instances (one created in xaml and another in code-behind). there 2 ways to fix the situation.
work with the instance created in xaml. to get reference to that instance cast DataContext to vm type:
public partial class MainWindow : Window
{
ViewModel viewmodel;
public MainWindow()
{
InitializeComponent();
viewModel = (ViewModel)DataContext;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
viewmodel.AddClient();
}
}
work with the instance created in code. set DataContext from code too:
public partial class MainWindow : Window
{
ViewModel viewmodel = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = viewmodel;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
viewmodel.AddClient();
}
}
and remove xaml part:
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>

checkbox checked or click event not firing in datatemplate

I have a checkbox in my datatemplate and for some reason the events are not firing. see code below. my datatemplate is in a resource dictionary with code behind file. Any Ideas?
<ResourceDictionary x:Class="ArmyBuilder.Templates.EquipmentDataTemplate"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<DataTemplate x:Key="EquipmentDataTemplate">
<Grid>
<CheckBox Content="{Binding Name}" Checked="ToggleButton_OnChecked" Click="CheckBox_Click"/>
</Grid>
</DataTemplate>
//code behind
namespace ArmyBuilder.Templates
{
public partial class EquipmentDataTemplate : ResourceDictionary
{
public EquipmentDataTemplate()
{
InitializeComponent();
}
private void ToggleButton_OnChecked(object sender, RoutedEventArgs e)
{
// breakpoint not hit
}
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
// breakpoint not hit
}
}
}
I am not sure how you use it, but the your code works for me and the click event got fired. Check the following and if you still cannot find the point, share a repro project to show how you used it.
Template XAML:
<ResourceDictionary x:Class="App10.EquipmentDataTemplate"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<DataTemplate x:Key="EquipmentDataTemplate">
<Grid>
<CheckBox Content="Click Me" Checked="ToggleButton_OnChecked" Click="CheckBox_Click"/>
</Grid>
</DataTemplate>
</ResourceDictionary>
Template cs:
namespace App10
{
public sealed partial class EquipmentDataTemplate : ResourceDictionary
{
public EquipmentDataTemplate()
{
this.InitializeComponent();
}
private void ToggleButton_OnChecked(object sender, RoutedEventArgs e)
{
// breakpoint not hit
}
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
// breakpoint not hit
}
}
}
In MainPage.Xaml, use the template in a ListView:
<Page
x:Class="App10.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App10"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<local:EquipmentDataTemplate></local:EquipmentDataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView x:Name="listView" CanReorderItems="True" AllowDrop="True" ItemTemplate="{StaticResource EquipmentDataTemplate}">
</ListView>
</Grid>
</Page>
In MainPage cs:
namespace App10
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
var list = new ObservableCollection<string>();
for (int i = 0; i < 10; i++)
{
list.Add("Item " + i);
}
listView.ItemsSource = list;
}
}
}

How to Display Part of a set of Filtered Objects?

Sorry if I replicate, but I'm having a hard time wording this for a decent query.
I'm trying to filter a set of complex objects through an ICollectionView and then display each object that made it through, represented by one of their properties. In this particular example, the collection is a list of ComplexClass objects, each of which containing a Name and Number property. I wish to represent each in a ListBox, sitting within a Popup, by their Name.
Here's the XAML code for the MainWindow:
<Window
x:Name="mainWindow"
x:Class="TestWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:test="clr-namespace:TestWPF"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
</Window.Resources>
<Grid x:Name="mainGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel x:Name="buttonStack">
<Button x:Name="popupButton" Click="popupButton_Click" Content="Pop-up!" VerticalAlignment="Top"/>
<Button x:Name="randomInsertButton" Click="randomInsertButton_Click" Content="Random Addition!"/>
</StackPanel>
<Popup x:Name="testPopup" PlacementTarget="{Binding ElementName=popupButton}" PopupAnimation="Scroll" Placement="Left"
AllowsTransparency="True" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top">
<Grid>
<Button x:Name="popupsButton" Content="Button" Width="75" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ListBox x:Name="testListBox" Height="100" DataContext="{Binding ElementName=mainWindow}" ItemsSource="{Binding Source=strings}" ScrollViewer.VerticalScrollBarVisibility="Visible" SelectionChanged="testListBox_Selected"/>
</Grid>
</Popup>
</Grid>
</Window>
The MainWindow's code-behind:
namespace TestWPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
static List<ComplexClass> stringList = new List<ComplexClass>();
public ObservableCollection<ComplexClass> strings;
public MainWindow()
{
InitializeComponent();
stringList.Add(new ComplexClass("apple",0));
stringList.Add(new ComplexClass("bat",2));
stringList.Add(new ComplexClass("cattle",6));
stringList.Add(new ComplexClass("dogma",5));
strings = new ObservableCollection<ComplexClass>(stringList);
Binding binding = new Binding();
binding.Source = strings;
testListBox.SetBinding(ListBox.ItemsSourceProperty, binding);
}
private void popupButton_Click(object sender, RoutedEventArgs e)
{
ICollectionView view = CollectionViewSource.GetDefaultView(strings);
view.Filter =
//null;
(o) =>
{
return (o as ComplexClass).Name!=string.Empty;
};
view.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Descending));
testPopup.IsOpen = !testPopup.IsOpen;
}
private void randomInsertButton_Click(object sender, RoutedEventArgs e)
{
Random r = new Random();
stringList.Add(stringList[r.Next(0, stringList.Count)]);
strings.Add(stringList.Last());
}
private void testListBox_Selected(object sender, RoutedEventArgs e)
{
ComplexClass selected =(ComplexClass)(sender as ListBox).SelectedItem;
stringList.Add(selected);
strings.Add(selected);
}
}
}
And finally, the ComplexClass code:
namespace TestWPF
{
public class ComplexClass
{
public string Name { get; private set; }
public int Number { get; private set; }
public ComplexClass(string name, int number)
{
Name = name;
Number = number;
}
}
}
What it's currently doing is displaying each of the objects as if they've been ToString()-ed: "TestWPF.ComplexClass".
I actually want them to be displayed as:
dogma
cat
bat
apple
in that order.
Can I get some help with this?
A couple of issues here.
First, you are binding to the list in your XAML:
<ListBox ... ItemsSource="{Binding Source=strings}" ... />
but then also setting the binding in code:
Binding binding = new Binding();
binding.Source = strings;
testListBox.SetBinding(ListBox.ItemsSourceProperty, binding);
You need to do one or the other: I'd suggest binding in XAML, and removing the binding stuff from the code-behind.
Second, the ComplexClass items are being ToString-ified because that's the default behaviour of a ListBoxItem when supplied with content (the items in a WPF ListBox are wrapped inside ListBoxItem elements). The easiest way for you to fix this is to set the DisplayMemberPath property on the ListBox:
<ListBox ... ItemsSource="{Binding Source=strings}" DisplayMemberPath="Name" ... />

Cannot add rows to DataGrid after initialization

I am rather new to WPF (Visual Studio Express 2012), and like much of it, it's cool but it's not coming as easily as I would expect. Thanks to stackoverflow and examples and tutorials I'm picking it up, but on this I'm stymied.
I have a datagrid, I bind it to a list, and I expect that when I add something to the list, it shows up in the datagrid. That happens in the MainWindow function, but doesn't happen in my code to handle a button click (it used to work just fine when I had a ListBox, but a ListBox doesn't support checkboxes, at least not natively, so I want to convert it).
I'm wondering if a side note in this tutorial is important - it says the ItemSource refers to the original list, but the Items property is a converted ItemCollection. Stepping thru the code, I can see MyList gets the new items when I click the button, but it just doesn't show up in the UI.
Please help!
DataGridClass.cs:
namespace WpfTest
{
class DataGridClass
{
public bool CheckboxColumn { get; set; }
public string Text1Column { get; set; }
public string Text2Column { get; set; }
}
}
MainWindow.xaml.cs:
namespace WpfTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
List<DataGridClass> myList = new List<DataGridClass>();
public MainWindow()
{
InitializeComponent();
MyDataGrid.ItemsSource = myList;
// this works
myList.Add(new DataGridClass()
{
CheckboxColumn = false,
Text1Column = "Initialization",
Text2Column = "ABCD"
});
}
private void MyButton_Click(object sender, RoutedEventArgs e)
{
// this doesn't work
myList.Add(new DataGridClass()
{
CheckboxColumn = false,
Text1Column = "Button Clicked",
Text2Column = "1234"
});
}
}
}
MainWindow.xaml:
<Window x:Class="WpfTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button x:Name="MyButton" Content="Populate Chart" HorizontalAlignment="Left" Margin="75,36,0,0" VerticalAlignment="Top" Width="120" Click="MyButton_Click"/>
<DataGrid x:Name="MyDataGrid" HorizontalAlignment="Left" Margin="75,76,0,0" VerticalAlignment="Top" Height="151" Width="349"/>
</Grid>
</Window>
You'll need to use ObservableCollection instead of List
ObservableCollection<DataGridClass> myList = new ObservableCollection<DataGridClass>();
it implements INotifyCollectionChanged interface which allows UI to pick up changes made to collection

Simple Binding of Data from code behind to XAML

I am new to WPF concepts. I want to just display a string in a textbox. I tried the following C# code and XAML to bind a string to a TextBox.Text property.
C# code:
public partial class Window1 : Window
{
public int TmpVal;
public string TmpStr;
public Window1()
{
TmpVal = 50;
TmpStr = "Windows Created";
InitializeComponent();
this.DataContext = this;
}
private void viewButton_Click(object sender, RoutedEventArgs args)
{
TmpStr = "Button clicked";
}
}
}
XAML:
<Window x:Class="TestWPF.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="600" Width="800" x:Name="ThisWindow">
<Grid>
<TextBox Name="txtTest1" Margin="200,0,200,200" HorizontalAlignment="Left" Height="50" Width="200" Text="{Binding TmpStr, ElementName=ThisWindow}" />
<Button Name="butTest1" Click="viewButton_Click">Test123</Button>
</Grid>
</Window>
On execution I always get blank text in my textbox (even when I invoke the click event).
I browsed through the stackoverflow site but couldn't solve the problem (though many questions were close to this one)
Can someone suggest me if anything is overlooked or missed out?
Databinding does not work with fields. Use Properties instead:
public int TmpVal {get; set;}
public string TmpStr {get; set;}
Also if you want the textbox to automatically pick up changes from your data you would ideally need to implement INotifyPropertyChanged or make it a dependency property or have a XXXChanged event for each XXX property (this doesn't work anymore).
<Window x:Class="WpfApplication5.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" x:Name="ThisWindow">
<StackPanel>
<TextBox Name="txtTest1" Text="{Binding TmpStr, ElementName=ThisWindow}" />
<Button Name="butTest1" Click="viewButton_Click">Test123</Button>
</StackPanel>
</Window>
And the code behind:
public partial class Window1 : Window, INotifyPropertyChanged
{
public Window1()
{
this.TmpStr = "Windows Created";
this.InitializeComponent();
this.DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public string TmpStr { get; set; }
public int TmpVal { get; set; }
private void viewButton_Click(object sender, RoutedEventArgs args)
{
this.TmpStr = "Button clicked";
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("TmpStr"));
}
}
}

Categories

Resources