WPF Binding 2 Columns to ListView - c#

XAML:
<ListBox x:Name="MyTitleBox" ItemsSource="{Binding}"/>
<ListBox x:Name="MyInfoBox" ItemsSource="{Binding}"/>
In C#:
MyTitleBox.ItemsSource = new List<string>(new string[] {"ID:", "Info:", "More Info:"});
MyInfoBox.ItemsSource = new ObservableCollection<string>(MyMainInfo.ToString().Split(',').ToList<string>());
I currently have 2 list boxes next to each other because I need to handle their ItemsSource programmatically.
I know there must be a better way to merge the two. Essentially the list box "on the left" is the titles like ID: and the list box "on the right" is the information.
I thought I could do something like MyTitleBox.Columns.Add like I've seen but it won't let me do .Columns. I'm using .NET 4.

Here is an example with a more MVVM approach:
Domain (The type of items you want to put in your list):
public class Movie : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _title;
public string Title
{
get { return _title; }
set
{
if (_title != value)
{
_title = value;
RaisePropertyChanged("Title");
}
}
}
private string _info;
public string Info
{
get { return _info; }
set
{
if (_info != value)
{
_info = value;
RaisePropertyChanged("Info");
}
}
}
protected void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
ViewModel:
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<Movie> _movies;
/// <summary>
/// Collection of movies.
/// </summary>
public ObservableCollection<Movie> Movies
{
get { return _movies; }
set
{
if (_movies != value)
{
_movies = value;
RaisePropertyChanged("Movies");
}
}
}
/// <summary>
/// Constructor
/// </summary>
public MyViewModel()
{
Movies = new ObservableCollection<Movie>();
Movies.Add(new Movie() { Title = "Gravity", Info = "Gravity is about..." });
Movies.Add(new Movie() { Title = "Avatar", Info = "Avatar is about..." });
}
protected void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
XAML:
<Window x:Class="StackOverflow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:StackOverflow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Movies}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding Title}" /><Run Text=" - " /><Run Text="{Binding Info}" />
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Grid.Row="1" Content="Click To Change Info" Margin="5" Click="Button_Click" />
</Grid>
</Window>
Code Behind:
public partial class MainWindow : Window
{
public MyViewModel ViewModel { get; private set; }
public MainWindow()
{
InitializeComponent();
ViewModel = new MyViewModel();
DataContext = ViewModel;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Movie movie = ViewModel.Movies.FirstOrDefault();
if (movie != null)
{
movie.Info = "This is the new information";
}
}
}
Implementing INotifyPropertyChanged allows the code to notify the UI when something changes.
If you test this code out you will see that clicking the button updates the info for the first movie, and this changed is immediately reflected in the UI. (Normally you would use a convention like Commands for handling the button click, but for simplicity I did it this way)
Let me know if you have any questions.

Related

INotifyPropertyChanged Not Working, I need some guide

I made an application with Windows template studio, As MVVM,
The Problem exists in ShellPage which contains some Controls, 2 Image , TextBlock, the NavigationView, and of course the Frame that holds all other pages.
The code here is for the TextBlock, but the Problem same for the 2 Image controls also.
in ShellPage.xaml:
xmlns:myControls="using:Numbers_to_Text.MyControls"
d:DataContext="{d:DesignInstance Type=viewmodels:ShellViewModel}"
Height="650" Width="1000" MaxHeight="650" MaxWidth="1000" MinHeight="650" MinWidth="1000"
mc:Ignorable="d" Background="{x:Null}">
<Page.Resources>
<helpers:AppSettings x:Key="AppSettings" />
</Page.Resources>
<TextBlock x:FieldModifier="public" x:Name="PageTitle" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="1"
Canvas.ZIndex="2" TextAlignment="DetectFromContent" HorizontalTextAlignment="DetectFromContent"
VerticalAlignment="Bottom" FontWeight="Bold" Text="{Binding ChangeTitle, Mode=TwoWay}"/>
in ShellPage.xaml.cs:
public ShellPage()
{
InitializeComponent();
DataContext = ViewModel;
ViewModel.Initialize(shellFrame, navigationView, KeyboardAccelerators);
}
and in ShellViewModel.cs
private void OnItemInvoked(WinUI.NavigationViewItemInvokedEventArgs args)
{
if (args.IsSettingsInvoked)
{
NavigationService.Navigate(typeof(SettingsPage), null, args.RecommendedNavigationTransitionInfo);
ChangeTitle = "Settings";
}
else
{
var selectedItem = args.InvokedItemContainer as WinUI.NavigationViewItem;
var pageType = selectedItem?.GetValue(NavHelper.NavigateToProperty) as Type;
if (pageType != null)
{
NavigationService.Navigate(pageType, null, args.RecommendedNavigationTransitionInfo);
ChangeTitle= pageType.Name;
}
}
}
private string _changeTitle;
public string ChangeTitle
{
get { return _changeTitle= GetTitle(); }
set
{
_changeTitle = value;
RaisePropertyChanged(nameof(ChangeTitle));
}
}
private static string GetTitle()
{
try
{
var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();
return NavigationService.Frame.Content != null
? resourceLoader.GetString(NavigationService.Frame.Content.GetType().Name)
: "Error Page Title";
}
catch
{
return "Welcome to Main Page";
}
}
public event PropertyChangedEventHandler propertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propName = "")
{
propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
public void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
this.propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Why ChangeTitle not changing when navigation occured?
I used breakPoints to trace the ChangeTitle, I implemented PropertyChangedEventHandler inside the shellViewModel instead to make sure that the property setter is call the NotifyPropertyChanged, with no luck.

TextBlock doesn't show Text even after Binding

I wanted to add a list of text to ListBox as soon as the user press the Button..Each ListItem contains TextBlock to which am binding the data..
But the TextBlock is not showing the text! Though I could see the Background color of each Item being inserted!
<StackPanel>
<Button Content="CLICK" Click="Button_Click"></Button>
<ListBox x:Name="dataList" Foreground="Red" Background="Blue">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Feed}" FontSize="28"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
My code behind looks like
public partial class MainPage : UserControl
{
ObservableCollection<Data> data;
public MainPage()
{
InitializeComponent();
data = new ObservableCollection<Data>();
dataList.ItemsSource = data;
}
class Data :INotifyPropertyChanged
{
public Data(String s)
{
Feed = s;
}
private string _feed;
public String Feed
{
get { return _feed; }
set { _feed = value; NotifyPropertyChanged("Feed"); }
}
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
data.Add(new Data("News1"));
data.Add(new Data("News2"));
data.Add(new Data("News2"));
}
}
Thanks..
Your class Data needs to be public else it would have private access specifier by default..
So it should be
public class Data.....
Everything else seems to be ok..

WPF Data binding Label content

I'm trying to create a simple WPF Application using data binding.
The code seems fine, but my view is not updating when I'm updating my property.
Here's my XAML:
<Window x:Class="Calculator.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:calculator="clr-namespace:Calculator"
Title="MainWindow" Height="350" Width="525"
Name="MainWindowName">
<Grid>
<Label Name="MyLabel" Background="LightGray" FontSize="17pt" HorizontalContentAlignment="Right" Margin="10,10,10,0" VerticalAlignment="Top" Height="40"
Content="{Binding Path=CalculatorOutput, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Window>
Here's my code-behind:
namespace Calculator
{
public partial class MainWindow
{
public MainWindow()
{
DataContext = new CalculatorViewModel();
InitializeComponent();
}
}
}
Here's my view-model
namespace Calculator
{
public class CalculatorViewModel : INotifyPropertyChanged
{
private String _calculatorOutput;
private String CalculatorOutput
{
set
{
_calculatorOutput = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I'm can't see what I am missing here?? o.O
CalculatorOutput has no getter. How should the View get the value? The Property has to be public as well.
public String CalculatorOutput
{
get { return _calculatorOutput; }
set
{
_calculatorOutput = value;
NotifyPropertyChanged();
}
}

EASY way to refresh ListBox in WPF?

I have created a simple form that inserts/updates/deletes a values for Northwind Customers.
Everything works fine, except in order to see a results, I have to close it, and reopen again.
My form looks like this :
I've searched tens of articles on how to refresh ListBox, but all of those use interface implementing, or using DataSets, and stuff I have never heard of and cannot implement. It's a very simple project, using simple procedures. Is there an easy way to refresh the list of customers without adding many lines of code?
The simple answer is: myListBox.Items.Refresh();
Are you using ObservableCollection and does your model implement INotifyPropertyChanged these two things will automaticly update the ListBox on any change. no need to explicitly refresh the list.
Here is a small example of using ObservableCollection and INotifyPropertyChanged, obviously you will populate your ObservableCollection from your SQL database.
Window:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ObservableCollection<MyModel> _list = new ObservableCollection<MyModel>();
private MyModel _selectedModel;
public MainWindow()
{
InitializeComponent();
List.Add(new MyModel { Name = "James", CompanyName = "StackOverflow"});
List.Add(new MyModel { Name = "Adam", CompanyName = "StackOverflow" });
List.Add(new MyModel { Name = "Chris", CompanyName = "StackOverflow" });
List.Add(new MyModel { Name = "Steve", CompanyName = "StackOverflow" });
List.Add(new MyModel { Name = "Brent", CompanyName = "StackOverflow" });
}
public ObservableCollection<MyModel> List
{
get { return _list; }
set { _list = value; }
}
public MyModel SelectedModel
{
get { return _selectedModel; }
set { _selectedModel = value; NotifyPropertyChanged("SelectedModel"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Xaml
<Window x:Class="WpfApplication11.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" Name="UI">
<Grid>
<ListBox ItemsSource="{Binding ElementName=UI, Path=List}" SelectedItem="{Binding ElementName=UI, Path=SelectedModel}" Margin="0,0,200,0" DisplayMemberPath="DisplayMember" SelectedIndex="0" />
<StackPanel HorizontalAlignment="Left" Height="100" Margin="322,10,0,0" VerticalAlignment="Top" Width="185">
<TextBlock Text="Name" />
<TextBox Height="23" TextWrapping="Wrap" Text="{Binding ElementName=UI, Path=SelectedModel.Name, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Company Name" />
<TextBox Height="23" TextWrapping="Wrap" Text="{Binding ElementName=UI, Path=SelectedModel.CompanyName, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Grid>
</Window>
Model
public class MyModel : INotifyPropertyChanged
{
private string _name;
private string _companyName;
public string Name
{
get { return _name; }
set { _name = value; NotifyPropertyChanged("Name"); }
}
public string CompanyName
{
get { return _companyName; }
set { _companyName = value; NotifyPropertyChanged("CompanyName"); }
}
public string DisplayMember
{
get { return string.Format("{0} ({1})", Name, CompanyName); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
PropertyChanged(this, new PropertyChangedEventArgs("DisplayMember"));
}
}
}
In this case any edit to properties will Update your list instantly, also will update when new Items are added/removed.
How about calling ListBox.UpdateLayout?
Of course you also need to update the particular item(s) so that it returns the updated string from the ToString method.
UPDATE: I think you also need to call ListBox.InvalidateArrange before you call ListBox.UpdateLayout.
Use INotifyPropertyChanged is the best way, refresh the entire list is not a good idea.
Main entrance:
public partial class MainWindow : Window
{
private BindingList<FoodModel> foodList = new BindingList<FoodModel>();
public MainWindow()
{
InitializeComponent();
}
private void Button1_Click(object sender, RoutedEventArgs e)
{
foodList.Add(new FoodModel { foodName = "apple1" });
foodList.Add(new FoodModel { foodName = "apple2" });
foodList.Add(new FoodModel { foodName = "apple3" });
FoodListBox.ItemsSource = foodList;
}
private void Button2_Click(object sender, RoutedEventArgs e)
{
foodList[0].foodName = "orange";
}
private void RefreshButton_Click(object sender, RoutedEventArgs e)
{
FoodListBox.Items.Refresh();
}
}
Model:
public class FoodModel: INotifyPropertyChanged
{
private string _foodName;
public string foodName
{
get { return _foodName; }
set
{
if (_foodName != value)
{
_foodName = value;
PropertyChanged(this, new PropertyChangedEventArgs("foodName"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
XAML:
<ListBox HorizontalAlignment="Center" Name="FoodListBox" VerticalAlignment="Top" Width="194" Height="150">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding foodName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Edit Cell in DataGrid (WPF)

I am new in WPF. I used to work in Winforms.
In Winforms I had the DataGridView that allows me to change, when I want a cell value.
Simply using:
dataGridView[columnIndex, rowIndex].Value = "New Value";
It works.
How can I accomplish this using DataGrid from WPF?
I was looking thorught stack over flow and could figure out an easy way to do this.
Thank you
Ok the simplest way to handle DataGrid is by binding to an ItemSource.
The example below shows how to bind your list and how changes upadte the DataGrid.
public partial class MainWindow : Window
{
private ObservableCollection<ConnectionItem> _connectionitems = new ObservableCollection<ConnectionItem>();
public MainWindow()
{
InitializeComponent();
ConnectionItems.Add(new ConnectionItem { Name = "Item1", Ping = "150ms" });
ConnectionItems.Add(new ConnectionItem { Name = "Item2", Ping = "122ms" });
}
public ObservableCollection<ConnectionItem> ConnectionItems
{
get { return _connectionitems; }
set { _connectionitems = value; }
}
private void button1_Click(object sender, RoutedEventArgs e)
{
// to change a value jus find the item you want in the list and change it
// because your ConnectionItem class implements INotifyPropertyChanged
// ite will automaticly update the dataGrid
// Example
ConnectionItems[0].Ping = "new ping :)";
}
}
public class ConnectionItem : INotifyPropertyChanged
{
private string _name;
private string _ping;
public string Name
{
get { return _name; }
set { _name = value; NotifyPropertyChanged("Name"); }
}
public string Ping
{
get { return _ping; }
set { _ping = value; NotifyPropertyChanged("Ping"); }
}
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Notifies the property changed.
/// </summary>
/// <param name="property">The info.</param>
public void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Xaml:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication4"
xmlns:properties="clr-namespace:WpfApplication4.Properties"
Title="MainWindow" Height="300" Width="400" Name="UI" >
<Grid>
<DataGrid Name="dataGridView" ItemsSource="{Binding ElementName=UI,Path=ConnectionItems}" Margin="0,0,0,40" />
<Button Content="Change" Height="23" HorizontalAlignment="Left" Margin="5,0,0,12" Name="button1" VerticalAlignment="Bottom" Width="75" Click="button1_Click" />
</Grid>
</Window>
i added a button to show how the data updates when you change something in your list, The class ConnectionItem is where you will store all your info for the datagrid.
Hope this helps

Categories