WPF DataGrid in textbox - c#

I have a WPF DataGrid in XAML and C # and I want to select a row and display the row in the text box, it is not a DataGridView
x:Name="dtGConsultas" ItemsSource= "{Binding }"
HorizontalAlignment="Left" Margin="12,3,0,0" Grid.Row="6" VerticalAlignment="Top"
Grid.ColumnSpan="5" Height="111" Width="598" Grid.RowSpan="3"
SelectionChanged="dtGConsultas_SelectionChanged"/>

This can be done in several ways:
You can bind SelectedItem to some property and then display it
You can bind TextBox value to the DataGrid's SelectedItem
You can set the TextBox value on each call of SelectionChanged method
If You would be using MVVM pattern, You should pick 1st option.
Other 2nd an 3rd options are useful for You, but in bigger (complex) applications this solutions would cause issues to read the code easily & maintain it. Not recommended.
Examples:
MVVM approach
ViewModel file:
using using System.Collections.ObjectModel;
public class MyViewModel
{
//add implementation of INotifyPropertyChange & propfull
public ObservableCollection<MyItem> MySrcList { get; set; }
//add implementation of INotifyPropertyChange & propfull
public MyItem SelectedItem { get; set; }
}
View:
<UserControl ...
xmlns:local="clr-namespace:MyProject">
<UserControl.DataContext>
<local:MyProject />
</UserControl.DataContext>
...
<DataGrid
ItemsSource="{Binding MySrcList}"
SelectedItem="{Binding SelectedItem}"/>
Binding TB value to value of DataGrid's SelectedItem
Xaml file:
<Grid>
<DataGrid
x:Name="dtGConsultas"
ItemsSource="{Binding MySrcList}"/>
<TextBox Text="{Binding dtGConsultas.SelectedItem, Mode=OneWay}"/>
</Grid>
Code-behind (C# file):
public class MyUserControl
{
public MyUserControl()
{
this.InitializeComponent();
this.DataContext = this;
}
public List<MyItem> MySrcList = new List<MyItem>();
}
Update in method (Code-behind):
Xaml file:
<Grid>
<DataGrid
x:Name="dtGConsultas"
ItemsSource="{Binding MySrcList}"
SelectionChanged="dtGConsultas_SelectionChanged"/>
<TextBox x:Name="MyTbx"/>
</Grid>
Code-Behind (C# file):
public class MyUserControl
{
public MyUserControl()
{
this.InitializeComponent();
this.DataContext = this;
}
public List<MyItem> MySrcList = new List<MyItem>();
private void dtGConsultas_SelectionChanged( /* args */)
{
MyTbx.Text = dtGConsultas.SelectedItem.ToString();
}
}

You can also add a column that contains a checkbox and you bind it. Then juste check if (Your_List.element.CheckBox==true). you can get a list whit your checked elements

Related

Binding to ViewModel on ListView shows nothing but works fine on other controls

I'm trying to bind an ObservableCollection sitting in my ViewModel class to a ListView that uses a GridView for displaying rows but I am unable to show my data in the list.
For testing purposes I put a TextBlock and can successfully show the data with Binding (however only if I do DataContext = ViewModel in code behind first, doing ViewModel.MyData in XAML does not work)
I've noticed that whatever ItemsSource is bound to, there is always a random number of empty element showing in the list, I can tell that since when I hover my mouse on the ListView the rows highlight. This number does not match the capacity of my collection.
If in code behind I manually set the ItemsSource to the collection I want to display, the correct number of element then shows up but there are still no data displayed.
XAML
<Page x:Class="GestionStockWPF.MainPage"
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:local="clr-namespace:GestionStockWPF"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="MainPage"
ShowsNavigationUI="False">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="13*"/>
<RowDefinition Height="77*"/>
</Grid.RowDefinitions>
<!-- The Binding here seems to do nothing, there isn't the correct amount of rows in the app -->
<ListView x:Name="listView"
HorizontalAlignment="Stretch"
Grid.Row="1" Margin="30, 0, 30, 30"
ItemsSource="{Binding Source=Tests}">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Path=A}"
Header="Asset Number" Width="200"/>
</GridView>
</ListView.View>
</ListView>
<!-- This textblock is just here for testing purposes, it successfully shows "A" -->
<TextBlock x:Name="textBlock" Text="{Binding Path=Tests[0].A}"
HorizontalAlignment="Left" Margin="252,28,0,0" TextWrapping="Wrap"
VerticalAlignment="Top"/>
</Grid>
</Page>
Code Behind File
using System.Windows;
using System.Windows.Controls;
namespace GestionStockWPF
{
public partial class MainPage : Page
{
public MainPageViewModel ViewModel;
public MainPage()
{
InitializeComponent();
ViewModel = new MainPageViewModel();
// If I do DataContext = this;
// and then set the binding of the TextBlock to "ViewModel.Tests[0].A" nothing is shown
DataContext = ViewModel;
// If I don't do that the listview does not contain the correct number of rows
listView.ItemsSource = ViewModel.tests;
}
}
}
ViewModel & Test class
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace GestionStockWPF
{
// Not sure if I have to implement INotifyPropertyChanged but my real class does it
public class Test : INotifyPropertyChanged
{
public string A { get { return "A"; } }
public string B { get { return "B"; } }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
public class MainPageViewModel
{
public ObservableCollection<Test> Tests { get; set; }
public MainPageViewModel()
{
tests = new ObservableCollection<Test>();
for (int i = 0; i < 50; ++i)
{
Tests.Add(new Test());
}
}
}
}
Your binding is incorrect.
It should be:
ItemsSource="{Binding tests}"
You need to specify the path in the binding, not the binding source.
The explicit Path part is optional in a binding, so you also can change your text block binding to:
Text="{Binding tests[0].A}"
By the way, the properties should use PascalCase, so better call it Tests.

C# wpf listbox not updating from ObservableCollection

I'm trying to get the databinding I need to work with a ListBox.
I've parsed some data from a text file to a ObservableCollection<ViewModel> but the data isn't updating in the ListBox.
Here's some information:
The data which is written to from the parser:
class MainData
{
private static ObservableCollection<GroupViewModel> groupModelList = new ObservableCollection<GroupViewModel>();
public static ObservableCollection<GroupViewModel> GroupModelList
{
get { return groupModelList; }
}
}
What GroupViewModel holds (not everything but it's all the same):
class GroupViewModel : INotifyPropertyChanged
{
private GroupModel groupModel;
public event PropertyChangedEventHandler PropertyChanged;
public GroupViewModel()
{
groupModel = new GroupModel();
}
public string Name
{
get { return groupModel.name; }
set
{
if (groupModel.name != value)
{
groupModel.name = value;
InvokePropertyChanged("Name");
}
}
}
...
}
And what GroupModel Holds:
class GroupModel
{
public string name { get; set; }
}
This is how the parser adds new items to the GroupModelView:
if (split[0] == "group")
{
currentGroup = new GroupViewModel();
currentGroup.Name = split[1];
MainData.GroupModelList.Add(currentGroup);
}
I created a ListBox in my WPF application with these XAML options:
<Window x:Class="SoundManager.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:SoundManager.ViewModels"
xmlns:vm2="clr-namespace:SoundManager.Code"
Title="MainWindow" Height="720" Width="1280">
<Window.Resources>
<vm:MainViewModel x:Key="MainViewModel" />
<vm2:MainData x:Key="MainData" />
</Window.Resources>
<ListBox Grid.Row="2" Height="484" HorizontalAlignment="Left" Margin="12,0,0,0" Name="lbFoundItems" VerticalAlignment="Top" Width="201" ItemsSource="{Binding Source={StaticResource MainData}, Path=GroupModelList/Name}" />
but for some reason the data isn't updating in the UI (new items aren't added visibly in the UI).
I've been just getting started with the MVVM pattern and databinding and I can't figure out what I'm doing wrong.
Thanks in advance!
GroupModelList/Name is not a valid property path here. Setting it like that does not make the ListBox show the Name property of the data items in the GroupModelList collection.
You would instead have to set the ListBox's DisplayMemberPath property:
<ListBox ItemsSource="{Binding Source={StaticResource MainData}, Path=GroupModelList}"
DisplayMemberPath="Name"/>
or set the ItemTemplate property:
<ListBox ItemsSource="{Binding Source={StaticResource MainData}, Path=GroupModelList}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Moreover, the GroupModelList property should not be static:
class MainData
{
private ObservableCollection<GroupViewModel> groupModelList =
new ObservableCollection<GroupViewModel>();
public ObservableCollection<GroupViewModel> GroupModelList
{
get { return groupModelList; }
}
}
Then you might have MainData as a property in your view model, and bind the ListBox like this:
<ListBox ItemsSource="{Binding Source={StaticResource MainViewModel},
Path=MainData.GroupModelList}" .../>

Bind dynamic data to wpf datagrid inside rowdetails template

I have a datagrid say Datagrid1 that is populated with a List (Dynamically through code behind). Inside the row details template for datagrid , i want to add a datagrid, lets calls it datagrid2, the datagrid2 needs to dynamically poupulated with List on the SelectionChange event of Datagrid1 ? Accessing the datagrid2 and binding it to a datasource needs to be done in the code behind. Can somone help me out with this?
my xaml is:
<Grid>
<my:DataGrid Name="dataGrid1" ItemsSource="{Binding}">
<my:DataGrid.RowDetailsTemplate>
<DataTemplate>
<my:DataGrid Name="dataGrid2"></my:DataGrid>
</DataTemplate>
</my:DataGrid.RowDetailsTemplate>
</my:DataGrid>
</Grid>
It would be much easier to do it via bindings. You could add a collection of DetailElements to each Element of your dataGrid1's ItemsSource. Now all you have to do is binding this collection to your dataGrid2' ItemsSource and it gets automatically populated with data through the binding.
public class DataGrid1SourceItem
{
public ObservableCollection<DetailItem> DetailItems {get;set;}
}
The XAML:
<Grid>
<my:DataGrid Name="dataGrid1" ItemsSource="{Binding}">
<my:DataGrid.RowDetailsTemplate>
<DataTemplate>
<my:DataGrid Name="dataGrid2" ItemsSource="{Binding Path=DetailItems}"></my:DataGrid>
</DataTemplate>
</my:DataGrid.RowDetailsTemplate>
</my:DataGrid>
</Grid>
EDIT: To search your database depending on the DataGrid cells value, you have to get this value into your ViewModel. To do this create a property (in my example ProductName) and bind it to the DataGridColumn's Binding property (Mode=TwoWay). Then you could have a private field which holds all products and filter this field in the dataGrid2 ItemsSources collection:
public class DataGrid1SourceItem
{
private List<DetailItems> _allDetailItems = new List<DetailItems>();
public IEnumerable<DetailItem> DetailItems
{
get { return _allDetailItems.Where(item => item.Name == ProductName); }
}
public DataGrid1SourceItem()
{
// load your products into _allDetailItems
}
private string _productName;
public string ProductName
{
get { return _productName; }
set
{
_productName= value;
OnPropertyChanged("ProductName");
OnPropertyChanged("DetailItems");
}
}
}
Following may help you
public partial class Window1 : Window
{
DataTable dt = new DataTable();
public Window1()
{
InitializeComponent();
dt.Columns.Add("Num1", typeof(string));
dt.Columns.Add("Num2", typeof(string));
dt.Rows.Add("100", "200");
dt.Rows.Add("300", "400");
this.dataGridTest.DataContext = dt;
this.dataGridTest.RowDetailsVisibilityChanged += new EventHandler<Microsoft.Windows.Controls.DataGridRowDetailsEventArgs>(dataGridTest_RowDetailsVisibilityChanged);
}
void dataGrid1_RowDetailsVisibilityChanged(object sender, Microsoft.Windows.Controls.DataGridRowDetailsEventArgs e)
{
Microsoft.Windows.Controls.DataGrid innerDataGrid = e.DetailsElement as Microsoft.Windows.Controls.DataGrid;
innerDataGrid.ItemsSource = ((IListSource)dt).GetList();
}
}
In XAML
<Grid>
<my:DataGrid Name="dataGridTest" ItemsSource="{Binding}">
<my:DataGrid.RowDetailsTemplate>
<DataTemplate>
<my:DataGrid Name="innerGrid"></my:DataGrid>
</DataTemplate>
</my:DataGrid.RowDetailsTemplate>
</my:DataGrid>
</Grid>

WPF databinding to an other class

I've created a WPF UI. The following code exists in MainWindow.xaml.cs:
namespace AWPFProject
{
public partial class MainWindow : Window
{
private readonly ServiceLogic serviceLogic;
public MainWindow()
{
InitializeComponent();
serviceLogic = new ServiceLogic ();
}
}
}
Servicelogic is my central class. From there, methods or classes are called to handle stuff like database management.
Now, that ServiceLogic class has the values I'd like to bind to.
For example, I have a combobox where I can show my users. The XAML looks like this:
<ListBox Height="100" HorizontalAlignment="Left" Margin="6,44,0,0"
Name="listBox_detected" VerticalAlignment="Top" Width="120"
ItemsSource="{Binding Path=ServiceLogic.Users}" />
When I run the application, the list remains emtpy. What else do I need to do to get that information in my list?
You need to change a few things to make this work in your scenario:
Set the correct DataContext for your window:
public MainWindow()
{
InitializeComponent();
DataContext = new ServiceLogic();
}
Make sure that ServiceLogic has a public property named Users:
public List<User> Users { get; set; }
if you want to add/remove items to this List at runtime, consider using an ObservableCollection<T> as this will notify the UI of any changes automatically.
Update the binding logic of your xaml, so that you bind to the correct list. Also set the DisplayMemberPath property or add a template so that the objects are displayed nicely:
<ListBox ItemsSource="{Binding Path=Users}" DisplayMemberPath="Name"/>
or
<ListBox ItemsSource="{Binding Path=Users}">
<ListBox.ItemTemplate>
<DataTemplate>
<...your data template, like grid or stackpanel/>
</DataTemplate>
</ListBox.DataTemplate>
When using DisplayMemberPath, make sure the User-class has the correct properties. Add the following to User.cs:
public string Name
{
get { return _name; }
set { _name = value; }
}
Here ItemsSource="{Binding Path=ServiceLogic.Users}" you state that data has public property ServiceLogic
Second, you data is acquired through DataContext
Change constructor:
public MainWindow()
{
InitializeComponent();
serviceLogic = new ServiceLogic ();
DataContext = serviceLogic;
}
and change binding to this one:
<ListBox Height="100" HorizontalAlignment="Left" Margin="6,44,0,0"
Name="listBox_detected" VerticalAlignment="Top" Width="120"
ItemsSource="{Binding Path=Users}" />
In Binding I removed ServiceLogic because SL stands as data item. And Path - is the path of the property.
I think you need to set "DisplayMemberPath" property of ListBox.

How to bind a Grid in WPF when using MVC?

I know a way to do MVC binding of one string to one TextBox. That's how it can be done:
C#:
namespace WpfApplication4
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = Model;
}
public ModelClass Model = new ModelClass();
private void button1_Click(object sender, RoutedEventArgs e)
{
Model.Output += "Setting New Output! ";
}
public class ModelClass : INotifyPropertyChanged
{
string _output;
public event PropertyChangedEventHandler PropertyChanged =
delegate { };
public string Output
{
get { return _output; }
set { _output = value;
PropertyChanged(this,
new PropertyChangedEventArgs("Output"));
}
}
}
}
}
XAML:
<Window x:Class="WpfApplication4.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 Content="Button" VerticalAlignment="Top"
Name="button1" Click="button1_Click" />
<TextBox VerticalAlignment="Bottom"
Name="textBox1" Text="{Binding Path=Output}" />
</Grid>
</Window>
But I can't find a way to bind a two-dimensional array (or List) to a Grid or DataGrid. Can you help me with it? I couldn't find a working example on SO.
Consider using a DataGrid to display your two-dimensional array, assuming you can store your data as a List<ColumnData> where ColumnData is a class with one property per table column.
The WPF SDK contains a DataGrid, and there are several data grids from vendors available that have additional features.
if you wanna bind data to a datagrid you should be read something about the following.
ICollectionView, BindingListCollectionView
if you have somekind of collection you simply set the itemssource.
<DataGrid ItemsSource="{Binding Path=MyCollection, Mode=OneWay}" />
Collection types are mostly ObservableCollection or DataSet/DataTable. if your collection supports editing and so on, you can do it with the datagrid.

Categories