Binding several buttons to individual items in an observable collection - c#

If I have an ObservableCollection in one of my classes. In my code behind my view I have an object of this class and use it as the DataBinding
this.DataContext = MyCustomClass;
in the xaml code of the view I want to bind several buttons to items in the Observable collection. Something like this:
<Button x:Name="Bid_Price_10" Grid.Row="0" Content="{Binding myObservableCollection[0].Price, Mode=OneWay}" Grid.ColumnSpan="2" HorizontalAlignment="Stretch" UseLayoutRounding="True" Padding="0"/>
<Button x:Name="Bid_Price_11" Grid.Row="1" Content="{Binding myObservableCollection[1].Price, Mode=OneWay}" Grid.ColumnSpan="2" HorizontalAlignment="Stretch" UseLayoutRounding="True" Padding="0"/>
At the moment this is not working, am I missing something ?
EDIT: Create full code to demo what I am trying to do:
So I have a Coffee class:
class Coffee
{
public int price { get; set; }
}
I have a drinks class that holds a list of coffees:
class Drinks
{
public List<Coffee> CoffeeList;
public Drinks()
{
CoffeeList = new List<Coffee>();
for (int i = 0; i < 10; i++)
{
Coffee c = new Coffee();
c.price = i;
CoffeeList.Add(c);
}
this.startCoffeePriceUpdateThread();
}
private void UpdateCofffeePrice()
{
while (true)
{
Thread.Sleep(1000);
foreach (var c in CoffeeList)
{
c.price++;
}
}
}
public void startCoffeePriceUpdateThread()
{
Thread coffeeThread = new Thread(new ThreadStart(UpdateCofffeePrice));
coffeeThread.Start();
}
}
my main window code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Drinks ourDrinks = new Drinks();
this.DataContext = ourDrinks;
}
}
and my xaml code:
<Grid x:Name="Grid" HorizontalAlignment="Left" Height="193" Margin="77,31,0,0" VerticalAlignment="Top" Width="315">
<Button x:Name="Button" Content="{Binding CoffeeList[0].Price}" HorizontalAlignment="Left" Margin="49,25,0,0" VerticalAlignment="Top" Width="75" Height="28"/>
</Grid>
So the problem is that I am not seeing anything in the button. At the moment I am not using INotifyPropertyChange as I have been advised it would not be needed.

As Clemens suggested: if everything is fixed, you won't need ObservableCollection nor PropertyChanged notification. The following code should be sufficient:
public class Stuff
{
public string Price { get; set; }
}
public class myCustomClass
{
public List<Stuff> myCollection { get; set; }
}
public partial class MainWindow : Window
{
public myCustomClass myCustomInstance = new myCustomClass();
public MainWindow()
{
InitializeComponent();
myCustomInstance.myCollection = new List<Stuff>();
myCustomInstance.myCollection.Add(new Stuff() { Price = "1200$" });
myCustomInstance.myCollection.Add(new Stuff() { Price = "5.5$" });
this.DataContext = myCustomInstance;
}
}
And the simplified XAML
<Button Content="{Binding myCollection[0].Price, Mode=OneWay}"/>
<Button Content="{Binding myCollection[1].Price, Mode=OneWay}" />
NB: I specified an instance of myCustomClass as the DataContext. If you really need to bind it to the class, let me know and I will update the code.

Related

Populating a ComboBox with a Array in C#

My Array is a list of Pokemon Names read from a text file and then stored into an Array in the PokemonData class seen below
private string[] pokemonNames;
private StreamReader readNames;
public PokemonData()
{
readNames = new StreamReader(setDirectory() + #".\PokemonNames.txt");
pokemonNames = new string[256];
populateArray(pokemonNames, readNames);
}
public string[] populateArray(string[] pokemonNames, StreamReader readNames)
{
string pokemonName = readNames.ReadLine();
int i = 0;
while (pokemonName != null)
{
pokemonNames[i] = pokemonName.Trim();
pokemonName = readNames.ReadLine();
i++;
}
readNames.Close();
return pokemonNames;
}
public string[] getPokemonNames()
{
return pokemonNames;
}
What I want to do is now populate an Combobox using WPF with all the names inside the array. I have tried googling this and frequently alot of the answers have classes setup much like this:
Class ExampleClass {
Public ExampleClass() {
string PokemonName; {get; set;}
}
}
I believe there is an assignment going on here, but I am unsure. C# isn't my usual language and this is my first time creating a gui. Could someone please guide me through so I could finish this.
I have tried doing a handful of things such as the code below and Databinding. At this point I believe I am missing something.
<Window
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:StarterEdit"
xmlns:Collections="clr-namespace:System.Collections;assembly=System.Runtime.Extensions" x:Class="StarterEdit.MainWindow"
mc:Ignorable="d"
Title="Starter Edit" Height="420" Width="550">
<Grid Margin="0,0,0,11" HorizontalAlignment="Center" Width="530">
<Label Content="Squirtle" HorizontalAlignment="Left" Margin="45,50,0,0" VerticalAlignment="Top" ToolTip="Starter One"/>
<Label Content="Bulbasaur" HorizontalAlignment="Left" Margin="245,50,0,0" VerticalAlignment="Top" ToolTip="Starter Two"/>
<Label Content="Charmander" HorizontalAlignment="Left" Margin="445,50,0,0" VerticalAlignment="Top" ToolTip="Starter Three"/>
<ComboBox x:Name="NameList" HorizontalAlignment="Left" Margin="10,81,0,0" VerticalAlignment="Top" Width="120" IsReadOnly="True" SelectedIndex="0" Cursor="Arrow" IsTextSearchEnabled="True" ToolTip="List of Pokemon names">
</ComboBox>
</Window>
Here is my MainWindow class
public partial class MainWindow : Window
{
Dictionary<int, string> pokemonNames = new Dictionary<int, string>();
PokemonData pokemonData = new PokemonData();
public MainWindow()
{
InitializeComponent();
NameList.ItemsSource = pokemonData.getPokemonNames(); //method that returns string array
NameList.ItemsSource = pokemonNames; //this is a dictionary
}
}
What I'm trying to do is using WPF I want to populate my comboBox with the data from the PokemonData Class, specifically the array containing all the names. The problem is whenever I bind the data or set the data it never displays on the gui or in the comboBox.
If shortly, the next code must work correctly, just do this initialization after loading data from the file.
NameList.ItemsSource = pokemonData.getPokemonNames();
If you want a better solution, you can find it below (when Pokemons collection have changed UI would be updated automatically):
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new PokemonData(setDirectory() + #".\PokemonNames.txt");
}
}
public class Pokemon
{
public int ID { get; set; }
public string Name { get; set; }
}
public class PokemonData
{
public ObservableCollection<Pokemon> Pokemons { get; set; } = new ObservableCollection<Pokemon>();
public PokemonData(string path)
{
LoadData(path);
}
private void LoadData(string path)
{
Pokemons.Clear();
using (StreamReader stream = new StreamReader(path))
{
int i = 1;
while (true)
{
string pokemonName = stream.ReadLine();
if (pokemonName != null)
Pokemons.Add(new Pokemon { ID = i, Name = pokemonName.Trim() });
else break;
i++;
}
}
}
}
And XAML code:
<ComboBox ItemsSource="{Binding Pokemons}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

ItemsControl using WrapPanel, not displaying anything

Goal: I am trying to create a wrap panel that has children that are bound to an observable collection.
Current Expected Behavior: I expect to see 3 nested wrap panels that have an ellipse, a text block, a label and a checkbox.
Problem: My wrap panel and contents are not displayed at runtime. (Note: "Test" and "Test 2" Labels outside of the itemscontrol do display as expected.)
I have read this and it doesn't seem to solve my problem.
Code Behind
using MVVM_SandBox.Models;
using MVVM_SandBox.ViewModels;
namespace MVVM_SandBox
{
public partial class MainWindow
{
public MainViewModel VMMain = new MainViewModel();
public MainWindow()
{
VMMain.SomeItemModelBlahs = new System.Collections.ObjectModel.ObservableCollection<ItemModelBlah>() { new ItemModelBlah() { Label = "blah0" }, new ItemModelBlah() { Label = "blah1", CoolStuff = true }, new ItemModelBlah() { Label = "blah2" } };
InitializeComponent();
}
}
}
XAML
<Window x:Name="winMain" x:Class="MVVM_SandBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewmodels="clr-namespace:MVVM_SandBox.ViewModels"
Title="MVVM SandBox" Height="600" Width="800" AllowDrop="True">
<Window.DataContext>
<viewmodels:MainViewModel></viewmodels:MainViewModel>
</Window.DataContext>
<StackPanel>
<Label Width="Auto" Height="Auto">Test</Label>
<ItemsControl ItemsSource="{Binding SomeItemModelBlahs}" Margin="10,10,10,10">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel x:Name="WrapPanelOfModelItems" Margin="10,10,10,10" Width="400" Height="200" IsItemsHost="True" MinWidth="200" MinHeight="200">
</WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Width="100" Height="100" Margin="10">
<Ellipse Width="10" Height="10" Fill="Aqua"></Ellipse>
<TextBlock Margin="10" Text="{Binding Label}"></TextBlock>
<Label>kjasdkjalsdjklsad</Label>
<CheckBox IsChecked="{Binding CoolStuff}">
<Label>Hello</Label>
</CheckBox>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Label Height="Auto" Width="Auto">Test 2</Label>
</StackPanel>
</Window>
View Model
using MVVM_SandBox.Models;
using System.Collections.ObjectModel;
namespace MVVM_SandBox.ViewModels
{
//[ImplementPropertyChanged]
public class MainViewModel
{
public ObservableCollection<ItemModelBlah> SomeItemModelBlahs { get; set; }
public MainViewModel()
{
}
}
}
Model
namespace MVVM_SandBox.Models
{
public class ItemModelBlah
{
public string Label { get; set; }
public bool CoolStuff { get; set; }
}
}
The code is creating two instances of MainViewModel: once in the code behind, and again in the XAML. The code behind instance has a non-null ObservableCollection, while the instance in the XAML is set as the DataContext.
I would suggest removing the viewmodels:MainViewModel from the XAML, and creating it solely in code:
public partial class MainWindow
{
public MainViewModel VMMain = new MainViewModel();
public MainWindow()
{
DataContext = VWMain;
InitializeComponent();
}
}
Also, it's a better design to set up the ObservableCollection from within the view model itself:
class MainViewModel
{
public ObservableCollection<ItemModelBlah> SomeItemModelBlahs { get; private set; }
public MainViewModel()
{
SomeItemModelBlahs = new ObservableCollection<ItemModelBlah>()
{
new ItemModelBlah() { Label = "blah0" },
new ItemModelBlah() { Label = "blah1", CoolStuff = true },
new ItemModelBlah() { Label = "blah2" }
};
}
}

WPF MVVM Navigate betweel views from the same hierarchical level

I am trying to make a small program mainly to learn MVVM. Its a small Book Library.
I have 4 views(and 4 viewmodels).
MainWindow is the parent view, where i display the other 3 view in a content control.
The other 3 child views are HomeView, BookManagingView, ReaderManagingView.
In HomeView, i display 2 ListViews(one with readers, one with books), and in the other 2 views i edit/add books or readers.
In my HomeView i also have 2 buttons. When i click the buttons i want to switch from the HomeView, to BookManagingView or ReaderManagingView.
If i am trying to switch to any of the Views from the MainWindow, it works.
What i am trying to do is to switch from the HomeView, to BookManagingView or ReaderManagingView. How can i achieve that?
MainWindow:
<Grid>
<ContentControl Content="{Binding CurrentView}" Height="340" Width="500" />
<Button x:Name="btnHomeView" Content="Home" Command="{Binding ChangeViewToHomeView, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Margin="16,70,0,0" VerticalAlignment="Top" Width="75"/>
<Button x:Name="btnBookManagingView" Content="Reader Options" Command="{Binding ChangeViewToReaderManagView,UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Margin="96,70,0,0" VerticalAlignment="Top" Width="92"/>
<Button x:Name="btnReaderManagingView" Content="Books Options" Command="{Binding ChangeViewToBookManagView,UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Margin="193,70,0,0" VerticalAlignment="Top" Width="92"/>
</Grid>
MainWindowVM:
public class MainWindowViewModel : ViewModelBase
{
private object currentView;
private HomeViewModel homeVM;
private ReaderManagingViewModel readerManagingVM;
private BookManagingViewModel bookManagingVM;
public MainWindowViewModel()
{
homeVM = new HomeViewModel();
readerManagingVM = new ReaderManagingViewModel();
bookManagingVM = new BookManagingViewModel();
CurrentView = homeVM;
ChangeViewToHomeView = new DefCommand(DisplayHomeView);
ChangeViewToReaderManagView = new DefCommand(DisplayReaderManagingView);
ChangeViewToBookManagView = new DefCommand(DisplayBookManagingView);
}
public DefCommand ChangeViewToHomeView { get; private set; }
public DefCommand ChangeViewToReaderManagView { get; private set; }
public DefCommand ChangeViewToBookManagView { get; private set; }
public object CurrentView
{
get { return currentView; }
set { currentView = value; RaisePropertyChanged(); }
}
public void DisplayHomeView()
{
CurrentView = homeVM;
}
public void DisplayReaderManagingView()
{
CurrentView = readerManagingVM;
}
public void DisplayBookManagingView()
{
CurrentView = bookManagingVM;
}
HomeView:
<Grid>
<ListView x:Name="listviewReaders" ItemsSource="{Binding ReadersList}" SelectedItem="{Binding SelectedReader, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="160" Margin="25,23,315,40">
...
<ListView x:Name="listviewBooks" ItemsSource="{Binding BookList, Mode=OneWay}" SelectedItem="{Binding SelectedBook, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="160" Margin="316,50,24,117">
...
<Button x:Name="btnEditReader" Command="{Binding EditReaderSwitch, UpdateSourceTrigger=PropertyChanged}" Content="EditR" HorizontalAlignment="Left" Margin="316,305,0,0" VerticalAlignment="Top" Width="74"/>
<Button x:Name="btnEditBook" Command="{Binding EditBookSwitch, UpdateSourceTrigger=PropertyChanged}" Content="EditB" HorizontalAlignment="Left" Margin="402,305,0,0" VerticalAlignment="Top" Width="74"/>
</Grid>
HomeVM:
private Reader selectedReader;
private Book selectedBook;
private BookListFilter selectedFilter;
private ObservableCollection<Book> bookList;
private ObservableCollection<Reader> readerList;
private IEnumerable<BookListFilter> bookLstItemSrc;
public HomeViewModel()
{
SelectedReader = new Reader();
SelectedBook = new Book();
SelectedFilter = BookListFilter.AllBooks;
BookDBDataContext rdb = new BookDBDataContext();
ReadersList = new ObservableCollection<Reader>(rdb.Readers);
GetBookList();
EditReaderSwitch = new DefCommand(EditReaderInfo);
EditBookSwitch = new DefCommand(EditBookInfo);
}
public DefCommand EditReaderSwitch { get; private set; }
public DefCommand EditBookSwitch { get; private set; }
private void EditBookInfo()
{
var tmpBook = new BookManagingViewModel(this);
var tmpMwvm = new MainWindowViewModel();
tmpMwvm.DisplayBookManagingView();
}
private void EditReaderInfo()
{
var tmpReader = new ReaderManagingViewModel(this);
var tmpMwvm = new MainWindowViewModel();
tmpMwvm.DisplayReaderManagingView();
}
Book & Reader ManagingViews have a bunch of textboxes and buttons for adding, deleting to/from database.
Book & Reader ManagingVM have methods for adding/deleting to/from database(right now they are empty and i will finish them if i manage to solve this problem first)
I have tried to navigate from HomeView to Book/ReaderManagingView with the EditBook/ReaderSwitch commands and EditBook/ReaderInfo() methods, but it doesnt work.
What am i doing wrong, and what should i do to fix it?
Sorry for the long post.
You need to set the CurrentView property of the existing MainWindowViewModel instance. Right now you are creating a new instance of the class.
You could inject the HomeViewModel with a MainWindowViewModel:
private readonly MainWindowViewModel _x;
public HomeViewModel(MainWindowViewModel x)
{
_x = x;
SelectedReader = new Reader();
...
}
private void EditBookInfo()
{
_x.DisplayBookManagingView();
}
MainWindowViewModel:
public MainWindowViewModel()
{
homeVM = new HomeViewModel(this);
}

Filling a ListBox from two TextBoxes with DataBinding

I have a ListBox I want to fill with data from two TextBoxesby clicking a Button. I think the problem comes from the differents textblock i have in my listbox. Here is what i want in image :
TheUI
The MainWindow.xaml of my listbox :
<ListBox x:Name="listBox"
ItemsSource="{Binding Issues}" Grid.Column="1" HorizontalAlignment="Left" Height="366" VerticalAlignment="Top" Width="453" Margin="0,0,-1,0">
<StackPanel Margin="3">
<DockPanel >
<TextBlock FontWeight="Bold" Text="Issue:"
DockPanel.Dock="Left"
Margin="5,0,10,0"/>
<TextBlock Text=" " />
<TextBlock Text="{Binding Issue}" Foreground="Green" FontWeight="Bold" />
</DockPanel>
<DockPanel >
<TextBlock FontWeight="Bold" Text="Comment:" Foreground ="DarkOrange"
DockPanel.Dock="Left"
Margin="5,0,5,0"/>
<TextBlock Text="{Binding Comment}" />
</DockPanel>
</StackPanel>
</ListBox>
My MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public sealed class ViewModel
{
public ObservableCollection<Issue> Issues { get; private set; }
public ViewModel()
{
Issues = new ObservableCollection<Issue>();
}
}
private void addIssue_Click(object sender, RoutedEventArgs e)
{
var vm = new ViewModel();
vm.Issues.Add(new Issue { Name = "Jon Skeet", Comment = "lolilol" });
DataContext = vm;
InitializeComponent();
}
}
My Issue.cs :
public sealed class Issue
{
public string Name { get; set; }
public string Comment { get; set; }
}
I follow this tutorial but i don't want to implement a Database :
Tuto
I also try to use this stackoverflow question
The error i have is 'System.InvalidOperationException' The Items collection must be empty to use ItemsSource
But not sure this is the heart of the problem.
Remove whatever you have inserted between <ListBox> and </ListBox>, as it is treated as part of Items collection.
Instead shift that content between <ListBox.ItemTemplate>...</ListBox.ItemTemplate>.
You don't need to update Context and InitializeComponent every time, atleast to your case.
public partial class MainWindow : Window
{
ViewModel vm = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = vm;
}
public sealed class ViewModel
{
public ObservableCollection<Issue> Issues { get; private set; }
public ViewModel()
{
Issues = new ObservableCollection<Issue>();
}
}
private void addIssue_Click(object sender, RoutedEventArgs e)
{
vm.Issues.Add(new Issue { Name = "Jon Skeet", Comment = "lolilol" });
}
}

How to use communication between ViewModels in WPF Catel framework?

When I click to register button (for example) in RegistrationViewModel or any other ViewModel, I would like to change the LeftSlide and MainSlide properties in MainWindowViewModel
LeftSlide = _leftSlides[0];
MainSlide = any other ViewModel;
I've read this documentation, but i can't implement this in my code
https://catelproject.atlassian.net/wiki/display/CTL/MVVM+communication+styles
How can i do this? Help me please
MainWindow.xaml
<DockPanel LastChildFill="True">
<DockPanel DockPanel.Dock="Top">
<Grid Width="120" DockPanel.Dock="Left">
<ContentControl Content="{Binding LeftSlide,
Converter={StaticResource ViewModelToViewConverter}}"></ContentControl>
</Grid>
<Grid DockPanel.Dock="Right">
<ContentControl Content="{Binding MainSlide,
Converter={StaticResource ViewModelToViewConverter}}"></ContentControl>
</Grid>
</DockPanel>
</DockPanel>
RegistrationViewModel.cs
public class RegistrationViewModel : ViewModelBase
{
public RegistrationViewModel()
{
RegistrationUserCommand = new Command(OnRegistrationUserCommandExecute);
}
public Command RegistrationUserCommand { get; private set; }
private void OnRegistrationUserCommandExecute()
{
//when I click this button, I need to change LeftSlide property in
// MainWindowViewModel to _leftSlides[1] (LeftSlide = _leftSlides[1];)
//for example
}
}
MainWindowViewModel.cs
public class MainWindowViewModel : ViewModelBase
{
private List<IViewModel> _leftSlides;
private List<IViewModel> _mainSlides;
public MainWindowViewModel()
{
_leftSlides = new List<IViewModel>
{
new NavViewModel(),
new AnotherNavViewModel()
//another ViewModels
};
_mainSlides = new List<IViewModel>
{
new RegistrationViewModel()
//another ViewModels
};
//Default ViewModels
MainSlide = _mainSlides[0];
LeftSlide = _leftSlides[0];
}
public IViewModel LeftSlide { get; set; }
public IViewModel MainSlide { get; set; }
}
I haven't any experience with WPF and Catel. It's our first project with my team. We are studying. And we can't stop using Catel Framework.

Categories