Databinding a Datagrid - c#

After being unsuccessful in databinding my Observable collection to my datagrid (Another question in this same forum), i tried to reduce the scope. Now my project has only one datagrid, one ObservableColection and one class.
But still my databinding is failing.. please help..
using System.Collections.ObjectModel;
using System.Windows;
namespace TestDatagrid
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class MainViewModel
{
public ObservableCollection<OptionStrike> oOs = new ObservableCollection<OptionStrike>(new OptionStrike[]
{
new OptionStrike("Put", 7500.00, 12345),
new OptionStrike("Call", 7500.00, 123),
new OptionStrike("Put", 8000.00, 23645),
new OptionStrike("Call", 8000.00,99999)
});
}
public class OptionStrike
{
public OptionStrike(string p1, double p2, int p3)
{
// TODO: Complete member initialization
this.Type = p1;
this.Strike = p2;
this.Volume = p3;
}
public string Type { get; set; }
public double Strike { get; set; }
public double Volume { get; set; }
}
}
this is my XAML..
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:TestDatagrid" x:Class="TestDatagrid.MainWindow" Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<DataGrid ItemsSource="{Binding oOs}" AutoGenerateColumns="True" />
</StackPanel>
</Grid>
</Window>

You need to expose your ObservableCollection as a Property, not a Field.
public class MainViewModel
{
public ObservableCollection<OptionStrike> oOs { get; set; }
public MainViewModel()
{
oOs = new ObservableCollection<OptionStrike>(new OptionStrike[]
{
new OptionStrike("Put", 7500.00, 12345),
new OptionStrike("Call", 7500.00, 123),
new OptionStrike("Put", 8000.00, 23645),
new OptionStrike("Call", 8000.00,99999)
});
}
}
You cannot bind to fields, see here for some more information on the subject.

Related

Binding to elements within a UserControl

C#, WPF.
I am trying to implement a UserControl with elements bound to properties in an object hierarchy. I have been using this as a reference.
I have created the following minimal example. It implements three instances of the UserControl, with the textbox in each case representing a filename. A dependency property is used to permit binding. Although it executes without errors, the textboxes are blank. They should contain "test1", "test2" and "test3". What am I missing?
Main window:
<Window x:Class="CustomControlTest.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:mycontrols="clr-namespace:MyControls"
mc:Ignorable="d"
Title="MainWindow" Height="100" Width="800">
<Grid>
<StackPanel Orientation="Vertical" DataContext="{Binding ElementName=parent}">
<mycontrols:DataFileControl x:Name="Ctrl1" FName="{Binding Path=project.File1.Filename}"/>
<mycontrols:DataFileControl x:Name="Ctrl2" FName="{Binding Path=project.File2.Filename}"/>
<mycontrols:DataFileControl x:Name="Ctrl3" FName="{Binding Path=project.File3.Filename}"/>
</StackPanel>
</Grid>
</Window>
namespace CustomControlTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
Project project = new Project();
public MainWindow()
{
InitializeComponent();
project.File1.Filename = "test1";
project.File2.Filename = "test2";
project.File3.Filename = "test3";
}
}
public class Project : INotifyPropertyChanged
{
public DataFile File1 { get; set; } = new DataFile();
public DataFile File2 { get; set; } = new DataFile();
public DataFile File3 { get; set; } = new DataFile();
public event PropertyChangedEventHandler PropertyChanged;
}
public class DataFile : INotifyPropertyChanged
{
public string Filename { get; set; } = "";
public event PropertyChangedEventHandler PropertyChanged;
}
}
UserControl:
<UserControl x:Class="MyControls.DataFileControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="40"
d:DesignWidth="800"
Name="TheCtrl">
<Grid Height="20">
<TextBox Name="filenameTextBox" Margin="5,0,5,0" Text="{Binding ElementName=TheCtrl, Path=FName, Mode=TwoWay}"/>
</Grid>
</UserControl>
namespace MyControls
{
public partial class DataFileControl : System.Windows.Controls.UserControl
{
public string FName
{
get { return (string)GetValue(FNameProperty); }
set { SetValue(FNameProperty, value); }
}
public static readonly DependencyProperty FNameProperty =
DependencyProperty.Register("FName", typeof(string), typeof(DataFileControl), new PropertyMetadata(null));
public DataFileControl()
{
InitializeComponent();
}
}
}
The expression
FName="{Binding Path=project.File1.Filename}"
requires a public property named project in the current DataContext, which you have not set. You should have noticed a data binding error message in the Output Window in Visual Studio when you debug the application.
Change it to
public Project project { get; } = new Project();
public MainWindow()
{
InitializeComponent();
project.File1.Filename = "test1";
project.File2.Filename = "test2";
project.File3.Filename = "test3";
DataContext = this;
}
Alternatively, use the Project instance as DataContext (and thus make it a view model)
private readonly Project project = new Project();
public MainWindow()
{
InitializeComponent();
project.File1.Filename = "test1";
project.File2.Filename = "test2";
project.File3.Filename = "test3";
DataContext = project;
}
and change the binding expressions to
FName="{Binding File1.Filename}"

WPF Caliburn.Micro and DXTabControl with UserControl not working

I'm trying to use a TabControl to switch between UserControls.
I could just set the content of the tabs to the usercontrols with XAML but then it will only be bound to the view and not the viewmodel.
My VM is a Caliburn.Micro Conductor and it calls ActivateItem whenever the user switches tabs. It worked fine when I only have one usercontrol, but when I created another one the first one will not load the view.
Here's some of the code I'm using:
ShellView:
<dx:ThemedWindow x:Class="PSCServiceManager.Views.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.caliburnproject.org"
xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Title="Service Manager" WindowState="Maximized"
Height="525" Width="720">
<Grid>
<dx:DXTabControl>
<dx:DXTabItem Header="Master Teknisi">
<ContentControl x:Name="LoadMasterTechnicianView" cal:View.Model="{Binding ActiveItem}" />
</dx:DXTabItem>
<dx:DXTabItem Header="Servisan">
<ContentControl x:Name="LoadServicesView" cal:View.Model="{Binding ActiveItem}" />
</dx:DXTabItem>
</dx:DXTabControl>
</Grid>
ShellViewModel:
using Caliburn.Micro;
namespace PSCServiceManager.ViewModels
{
public class ShellViewModel : Conductor<IScreen>
{
private MasterTechnicianViewModel masterTechnicianViewModel;
private ServicesViewModel servicesViewModel;
public ShellViewModel()
{
LoadMasterTechnicianView();
}
public void LoadMasterTechnicianView()
{
ActivateItem(masterTechnicianViewModel);
}
public void LoadServicesView()
{
ActivateItem(servicesViewModel);
}
}
}
An easier/alternative way to implement this would be to create a collection of User Controls you would like to bind to the Tab Control. For example,
public interface ITabUserControl
{
string DisplayName { get; set; }
}
public class MasterTechnicianViewModel : ITabUserControl
{
public string DisplayName { get; set; } = "Master Technician";
}
public class ServicesViewModel : ITabUserControl
{
public string DisplayName { get; set; } = "Services";
}
Now in your ShellViewModel, you could create a Collection of ITabUserControl
public List<ITabUserControl> UserControls { get; set; }
public ShellViewModel()
{
UserControls = new List<ITabUserControl>();
UserControls.Add(new MasterTechnicianViewModel());
UserControls.Add(new ServicesViewModel());
}
And bind your TabControl as
<dx:DXTabControl x:Name="UserControls"/>
Now you can switch between the controls without any issues, without Activating it explicitly.

Populate Listview using mvvm

i would like to ask how to Populate listview using MVVM pattern i'm beginner in mvvm pattern, i learn more from doing things than reading. i've done this before using wpf but i use code behind.
I use Mvvm Light. What i want is browse the Location of the folder and then populate the listview with the files inside it
so far i already have a Browse folder
i have this code
public class OpenFileDialogVM : ViewModelBase
{
public static RelayCommand OpenCommand { get; set; }
private string _selectedPath;
public string SelectedPath
{
get { return _selectedPath; }
set
{
_selectedPath = value;
RaisePropertyChanged("SelectedPath");
}
}
private string _defaultPath;
public OpenFileDialogVM()
{
RegisterCommands();
}
public OpenFileDialogVM(string defaultPath)
{
_defaultPath = defaultPath;
RegisterCommands();
}
private void RegisterCommands()
{
OpenCommand = new RelayCommand(ExecuteOpenFileDialog);
}
private void ExecuteOpenFileDialog()
{
var dialog = new FolderBrowserDialog();
dialog.ShowDialog();
SelectedPath = dialog.SelectedPath;
}
}
and i have this code for user control
<UserControl x:Class="MvvmLight1.FolderDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:MvvmLight1"
xmlns:local="clr-namespace:MvvmLight1"
mc:Ignorable="d" d:DesignWidth="300" Height="186.916" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="90*"/>
<RowDefinition Height="97*"/>
</Grid.RowDefinitions>
<Grid>
<TextBox Text="{Binding SelectedPath}" />
</Grid>
<Grid Grid.Row="1" >
<Button Command="vm:OpenFileDialogVM.OpenCommand" >Browse</Button>
</Grid>
</Grid>
</UserControl>
so far the browse is working. my question is how can i call this code. after the selection of folder so that i can populate my listview?
private void Call(string selectedpath)
{
try
{
var allFiles = Directory.GetFiles(selectedpath, "*", SearchOption.AllDirectories);
foreach (var item in allFiles)
{
System.Console.WriteLine(item);
//code for populating listview
}
}
catch (System.Exception ex)
{
System.Console.WriteLine(ex.StackTrace);
throw ex;
}
}
Thank you for time.
Make Files a public observable collection.
Raise the PropertyChanged event.
Set the window's datacontext to your viewmodel.
Bind to the viewmodel's property in xaml.
CS
using System;
using System.ComponentModel;
using System.IO;
using System.Windows;
using System.Windows.Input;
using System.Collections.ObjectModel;
namespace StackOverflow_PopulateListView
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new OpenFileDialogVM();
}
}
public class OpenFileDialogVM : ViewModelBase
{
public static RelayCommand OpenCommand { get; set; }
private string _selectedPath = "Enter a Path";
public string SelectedPath
{
get { return _selectedPath; }
set
{
_selectedPath = value;
OnPropertyChanged("SelectedPath");
}
}
private ObservableCollection<string> _files = new ObservableCollection<string>() { "Tits", "balls", "ass", "tits" };
public ObservableCollection<string> Files
{
get { return _files; }
set
{
_files = value;
OnPropertyChanged("Files");
}
}
private ICommand _selectFileCommand;
public ICommand SelectFileCommand
{
get
{
return _selectFileCommand ?? (_selectFileCommand = new RelayCommand(() => Call(SelectedPath)));
}
protected set
{
_selectFileCommand = value;
}
}
public void Call(string selectedpath)
{
try
{
Files = new ObservableCollection<string>(Directory.GetFiles(selectedpath, "*", SearchOption.AllDirectories));
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
throw ex;
}
}
}
public class RelayCommand : ICommand
{
public Action Act { get; set; }
/// <summary> Occurs when the target of the Command should reevaluate whether or not the Command can be executed. </summary>
public event EventHandler CanExecuteChanged;
public RelayCommand(Action act)
{
Act = act;
}
/// <summary> Returns a bool indicating if the Command can be exectued with the given parameter </summary>
public bool CanExecute(object obj)
{
return true;
}
/// <summary> Send a ICommand.CanExecuteChanged </summary>
public void ChangeCanExecute()
{
object sender = this;
EventArgs eventArgs = null;
CanExecuteChanged(sender, eventArgs);
}
/// <summary> Invokes the execute Action </summary>
public void Execute(object obj)
{
Act();
}
}
/// <summary>
/// Extremely generic ViewModelBase; for copy-pasting into almost any MVVM project
/// </summary>
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Fires PropertyChangedEventHandler, for bindables
/// </summary>
protected virtual void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}
XAML
<Window x:Class="StackOverflow_PopulateListView.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:StackOverflow_PopulateListView"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="8*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<ListView ItemsSource="{Binding Files}" Grid.Row="0">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<TextBox Grid.Row="1"
Text="{Binding SelectedPath}"
/>
<Button Grid.Row="2"
Content="Call"
Command="{Binding SelectFileCommand}"
/>
</Grid>
</Window>
Your view model should have an ObservableCollection of file names
public ObservableCollection<string> FileNames { get; }
= new ObservableCollection<string>();
which is populated when a directory is selected:
var files = Directory.EnumerateFiles(selectedpath, "*", SearchOption.AllDirectories);
FileNames.Clear();
foreach (var file in files)
{
FileNames.Add(file);
}
You would then bind the ItemsSource property of a ListBox to that collection:
<ListBox ItemsSource="{Binding FileNames}"/>

Missing something on WPF Binding

Just starting WPF & cannot get simple WPF Binding on DataGrid to work and I do not know how to debug. The bound class initializer executes but nothing shows on DataGrid. Minimum code behind and I kept the XAML & bound objects as simple as possible. Thank you for any help.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfBinding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" enter code heremc:Ignorable="d" x:Class="WpfBinding.MainWindow"
xmlns:syncfusion="http://schemas.syncfusion.com/wpf"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Window.Resources>
<local:Simple x:Key="keySimple"/>
</Window.Resources>
<Grid>
<DataGrid x:Name="dg" AutoGenerateColumns="True"
DataContext="{Binding Source={StaticResource keySimple}}"
ItemsSource="{Binding Path=Numbers}">
</DataGrid>
</Grid>
</Window>
namespace WpfBinding
{
public class Simple
{
public List<Number> Numbers = new List<Number>();
public Simple()
{
Numbers.Add(new Number(5));
Numbers.Add(new Number(6));
}
}
public class Number
{
private int nmb;
public Number(int x) { nmb = x; }
}
}
Bindings works only on properties and not member variables.
Just change your class to
public class Simple
{
public List<Number> _numbers = new List<Number>();
public List<Number> Numbers { get { return _numbers; } }
public Simple()
{
_numbers.Add(new Number(5));
_numbers.Add(new Number(6));
}
}
public class Number
{
public int NMB { get; set; }
public Number(int x) { NMB = x; }
}

How to bind class member in a collection

I want to bind a class member of an element I added to a collection to DisplayMemberPath. I bound a ObservableCollection to ComboBox.ItemSource and want to show the property name in the combobox's list which is a member of my class AxisBase.
Here is my code:
private ObservableCollection<AxisBase> axis { get; set; }
axis I use to hold elements of the following class
class AxisBase
{
...
public string name { get; set; }
...
}
This is how my xaml looks like
<ComboBox Name="comboBox_AchsenListe" DisplayMemberPath="{Binding ElementName=axis, Path=AxisBase.name}" ItemsSource="{Binding ElementName=_MainWindow, Path=axis}"</ComboBox>
Does anyone know how to bind name to DisplayMemberPath?
change DisplayMemberPath value
DisplayMemberPath="name"
SelectedValuePath="name"
and look at this question
I have created sample application for you
here the xaml
<Window x:Class="ComboBoxSample.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>
<ComboBox
ItemsSource="{Binding Path=AxisBases}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
Height="23" HorizontalAlignment="Left" Margin="200,134,0,0" Name="comboBox1" VerticalAlignment="Top" Width="120" />
</Grid>
here is code behind
using System.Collections.ObjectModel;
using System.Windows;
namespace ComboBoxSample
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
AxisBases = new ObservableCollection<AxisBase>
{
new AxisBase {Name = "Firts"},
new AxisBase {Name = "Second"},
new AxisBase {Name = "Third"}
};
//Set the data context for use binding
DataContext = this;
}
public ObservableCollection<AxisBase> AxisBases { get; set; }
}
public class AxisBase
{
public string Name { get; set; }
}
}
It works OK and binding also in combo box appears 3 items.

Categories

Resources