I am new with WPF and I am trying to add a new to the data grid I created.
The rows I am adding should be added dynamically however I can't see the values of the data in in the data grid.
Here is the xaml:
<Window x:Class="ProtocolAnalyzer.createByProtocol"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="createByProtocol" Height="506" Width="384">
<Grid Margin="0,0,2,4">
<DataGrid x:Name="dataGridTable" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Height="452" Width="245">
<DataGrid.Columns>
<DataGridTextColumn Header="Field"/>
<DataGridTextColumn Header="Value"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Here is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace ProtocolAnalyzer
{
/// <summary>
/// Interaction logic for createByProtocol.xaml
/// </summary>
public partial class createByProtocol : Window
{
private ProtocolData.ProtocolData.Protocols _runningProtocol;
public class Data
{
public string Name { get; set; }
public string Value { get; set; }
}
public createByProtocol(ProtocolData.ProtocolData.Protocols protocol)
{
InitializeComponent();
_runningProtocol = protocol;
buildTable();
}
private void buildTable()
{
switch (_runningProtocol)
{
case ProtocolData.ProtocolData.Protocols.ZBM:
dataGridTable.Items.Add("");
dataGridTable.Items[0] = "FFF";
break;
}
}
}
}
EDIT: some general information for "dynamic controls" in wpf/mvvm
if you go the MVVM style you do something like this.
viewmodel
//your data
public ObservableCollection<Customer> MySource {get;set;}
//Command to add a new row
public ICommand AddNewCustomerCommand {get{return _lazyAddCommand.Value;}}
private readonly Lazy<DelegateCommand> _lazyAddCommand;
//ctor
public MyViewmodel()
{
MySource = new ObservableCollection<Customer>();
_lazyAddCommand= new Lazy<DelegateCommand>(() => new DelegateCommand(AddNewCustomerCommandExecute, CanAddNewCustomerCommandExecute));
}
private bool CanAddNewCustomerCommandExecute()
{
return true;//your Conditions goes here
}
private void AddNewCustomerCommandExecute()
{
if (!CanAddNewCustomerCommandExecute())
return;
//Add new Customer
MySource.Add(new Customer());
}
view: use Binding to set the ItemsSource for your Datagrid
<DataGrid ItemsSource="{Binding MySource}">
<DataGrid.Columns>
<DataGridTextColumn Header="Field"/>
<DataGridTextColumn Header="Value"/>
</DataGrid.Columns>
</DataGrid>
thats all. the new row will display as far as your command is invoked through a button or something else
if you have data in "DataTable" that you are trying to assign to datagrid then you can use datagrid.Datasource porperty
If you have a list or an array..
then just use foreach loop and under that loop add you rows.
Related
I'm binding two comboboxes to the same listviewcollection. The problem is that selecting a value in one combobox, causes the other combobox selected item to change to the exact value of the first combobox. They are coupled and I want them to be independent of each other.
MyListViewCollection is like this
modelsView = new ListCollectionView(MainVM.All_Models);
All_Models is an Observable collection of custom objects like this
public ObservableCollection<MLModel> All_Models { get; set; } = new ObservableCollection<MLModel>() { };
I bind two ComboBoxes to modelsview like this
<ComboBox ItemsSource="{Binding Path=modelsView}" SelectedItem="{Binding SelectedModel_Right}" SelectionChanged="RightSideModelSelection">
<ComboBox ItemsSource="{Binding Path=modelsView}" SelectedItem="{Binding SelectedModel_Left}" SelectionChanged="LeftSideModelSelection">
So everything works fine, the comboboxes contain the identical lists of items from the models view which is what I want.
I definitely have bound the selected item to two separate properties in the view model, and those are
private MLModel _selectedModel_left;
public MLModel SelectedModel_Left
{
get { return _selectedModel_left; }
set
{
SetProperty(ref _selectedModel_left, value);
}
}
private MLModel _selectedModel_right;
public MLModel SelectedModel_Right
{
get { return _selectedModel_right; }
set
{
SetProperty(ref _selectedModel_right, value);
}
}
The only thing I can think of is that they are both referencing the same object in the collection, but I'm not exactly sure how it causes this behavior.
Also the desired behavior is that each combobox be able to select and display a different item from the same collection.
Any explanation would be helpful as well as the recommended way to decouple the two comboboxes selection from each other.
EDIT: As requested here is the code to create a minimal working example
MainWindow.xaml
<Window x:Class="WpfApplication3.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:WpfApplication3"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel Orientation="Vertical">
<ComboBox ItemsSource="{Binding Path=modelsView}" SelectedItem="{Binding SelectedModel_Left}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox ItemsSource="{Binding Path=modelsView}" SelectedItem="{Binding SelectedModel_Right}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication3
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
}
MLModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WpfApplication3
{
public class MLModel
{
public string Name { get; set; }
public string Type { get; set; }
}
}
ViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
namespace WpfApplication3
{
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ViewModel()
{
modelsView = new ListCollectionView(All_Models);
//modelsView.Filter = modelsFilter;
}
public ListCollectionView modelsView { get; set; }
public ObservableCollection<MLModel> All_Models { get; set; } = new ObservableCollection<MLModel>() {
new MLModel() { Name = "One", Type = "TypeOne" },
new MLModel() {Name = "Two", Type = "TypeTwo" },
new MLModel() {Name = "Three", Type = "TypeThree" }
};
private MLModel _selectedModel_left;
public MLModel SelectedModel_Left
{
get { return _selectedModel_left; }
set
{
this._selectedModel_left = value;
NotifyPropertyChanged();
}
}
private MLModel _selectedModel_right;
public MLModel SelectedModel_Right
{
get { return _selectedModel_right; }
set
{
this._selectedModel_right = value;
NotifyPropertyChanged();
}
}
}
}
From the documentation:
If the target is an ItemsControl, the current item is synchronized with the selected item.
You are sharing the same ListCollectionView between both ComboBox objects. This means that the current selected item is updated in the view, and replicated in any other ItemsControl where that same view is used.
If you don't want this behavior, you need to give each ComboBox its own ListCollectionView object to bind to.
Alternatively, you can set the IsSynchronizedWithCurrentItem property of each ComboBox to false.
I am new in wpf.
I have a datagrid, on application startup it have blank row where the user will enter data.
In my datagrid there is Product_Name column. I want when the user type any product name in this column an auto complete list open under this column and match string from Database Product Table. How can I do that ?
If it is not possible in DataGridTextColumn then how can i fill my DataGridComboBoxColumn with this Database Column in Product Table and this datagrid Combobox can search string. User can type in datagrid combobox the name of Product and if string match under this datagrid Combobox then matching string(Product Name) should bring up in combobox list.
I am searching since two days but no luck kindly guide me in detail not have command new in wpf and c#. Thanks.
xaml:
<DataGrid x:Name="dg_List_Of_PurchasedItem" Margin="5" AutoGenerateColumns="False" ItemsSource="{Binding Purch_Order}" AlternatingRowBackground="LightGray" >
<DataGrid.Columns>
<DataGridTextColumn Header="Sr#" Width="35" Binding="{Binding Sr}"></DataGridTextColumn>
<DataGridCheckBoxColumn Header="Select" Width="45" Binding="{Binding Select}"></DataGridCheckBoxColumn>
<DataGridComboBoxColumn Header="Product" x:Name="cb_ProdName" Width="150" SelectedValueBinding="{Binding Product_Name}"></DataGridComboBoxColumn>
<DataGridTextColumn Header="Description" Width="170" Binding="{Binding Description}" ></DataGridTextColumn>
<DataGridTextColumn Header="Type" Width="80" Binding="{Binding Type}" ></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
Code-behind:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class add_Purchase_Order_Win : Window
{
public add_Purchase_Order_Win()
{
InitializeComponent();
DataContext = new PO_ViewModel();
}
}
public class PO_ViewModel
{
private ObservableCollection<Purch_Order> _Purch_Order = new ObservableCollection<Purch_Order>();
public ObservableCollection<Purch_Order> Purch_Order
{
get { return _Purch_Order; }
set { _Purch_Order = value; }
}
}
public class Purch_Order
{
public Purch_Order() { }
public int Sr { get; set; }
public bool Select { get; set; }
public string Product_Name { get; set; }
public string Description { get; set; }
public string Type { get; set; }
}
}
I can't seem to find this answer via searching and cannot connect to the dots from other examples to my scenario.
Using MVVM Pattern:
How do I bind my model class via ViewModel in my view to a listbox/datagrid?
How do I pick and choose which columns I want displayed?
Question 2 Followup: Can I pick and choose via the visual Studio UI properties, if yes, how?
If done with XAML, is there autocomplete with my model/viewmodel class?
Using Observable Collection, what's the correct way to implement?
What is the "preferred" XAML control(datagrid?, listbox?, etc )
I've simplified my example into this:
Model Class:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace TestListBinding.Models
{
public class ProjectModel
{
public string Id { get; set; }
public string ProjectTitle { get; set; }
public string ProjectDetails { get; set; }
}
}
ViewModel Class:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Threading.Tasks;
using TestListBinding.Models;
namespace TestListBinding.ViewModels
{
public class ProjectViewModel
{
private ProjectModel _project;
private ObservableCollection<ProjectModel> _projects;
public ProjectViewModel()
{
_projects = new ObservableCollection<ProjectModel>();
var proj = new ProjectModel();
for (int i = 1; i <= 3; i++)
{
proj.Id = "ID" + i.ToString();
proj.ProjectTitle = "Project " + i.ToString();
proj.ProjectDetails = "Details about this: " + i.ToString();
_projects.Add(proj);
proj = new ProjectModel();
}
}
public IEnumerable<ProjectModel> Projects
{
get { return _projects; }
}
}
}
The View Part:
I know I need to have a DataContext for the view. Many examples show it being set in the code-behind(see below), and some example reference a "StaticResource" for a listbox. I've not found a way to setup the bindings via UI with the way my code is setup.
MainWindow.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using TestListBinding.ViewModels;
namespace TestListBinding
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ProjectViewModel();//is this correct, or is there an alternative method via XAML/control properties?
}
}
}
XAML(via code or UI) Can you please help me complete the data grid columns/bindings:
Updated per #EdPlunkett
<Window x:Class="TestListBinding.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:TestListBinding"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid HorizontalAlignment="Left" Height="239" Margin="42,32,0,0" VerticalAlignment="Top" Width="435" ItemsSource="{Binding Projects}">
<DataGrid.Columns>
<DataGridTextColumn Header="Project Title" Binding="{Binding ProjectTitle}"/>
<DataGridTextColumn Header="Project Details" Binding="{Binding ProjectDetails}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
This yields a strange layout with extra columns:
This is correct in your window constructor:
DataContext = new ProjectViewModel();
And this should show you some stuff in the grid:
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Id}" Header="ID" />
<DataGridTextColumn Binding="{Binding ProjectTitle}" Header="Project Title" />
<DataGridTextColumn Binding="{Binding ProjectDetails}" Header="Project Details" />
</DataGrid.Columns>
WPF people generally don’t use the designer/Properties pane stuff. It’s not great and it hasn’t improved in over ten years. You’re out of luck on autocomplete in this case as well.
Preferred control? Depends what you want: If you want multiple columns per row, use DataGrid or ListView. If you want one value displayed per row, or some kind of pretty templated UI per row, use ListBox.
Quick ListBox:
<ListBox
ItemsSource=“{Binding Projects}”
DisplayMemberPath=“ProjectTitle”
/>
I have a Datagrid with Bound Columns to a List(Of AnonymouseType), one is a CheckBox using DataGridTemplateColumn.CellTemplate, this is the XAML:
<DataGrid Name="dgServicios" Margin="15" AutoGenerateColumns="False"
AlternatingRowBackground="#CCEEF6"
SelectionMode="Single" SelectionUnit="Cell" TabIndex="20" >
<DataGrid.Columns>
<DataGridTemplateColumn Header="Selected" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Selected,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"
HorizontalAlignment="Center" Click="CheckBox_Click"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Codigo" Binding="{Binding Codigo}" Width="Auto" IsReadOnly="True"/>
<DataGridTextColumn Header="Nombre" Binding="{Binding Nombre}" Width="Auto" IsReadOnly="True"/>
</DataGrid.Columns>
</DataGrid>
The CheckBox works fine at runtime but the IsChecked property doesn't persist when I try to recover values from DB, the items Boolean property is ok, but none of Bound Checkboxes are checked. How Can I get the Checkboxes match with the items boolean property?? The Binding is wrong?
Can you please provide more information on the list of objects you are binding to?
From what I can tell, your bindings appear to be coded correctly. I tried copying and pasting your xaml into a new solution and then set the ItemsSource property it to an class I created that has your same column names, and everything appears to work correctly. The ObservableCollection of "GridItem" I'm bound to implements the INotifyPropertyChanged interface, and anytime a property gets updated, it notifies the UI. This is what I did to test out your xaml:
The code behind for MainWindow.xaml.cs:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ObservableCollection<GridItem> GridItems;
public MainWindow()
{
InitializeComponent();
GridItems = new ObservableCollection<GridItem>();
GridItems.Add(new GridItem { Codigo = "value1", Nombre = "Value2", Selected = false });
GridItems.Add(new GridItem { Codigo = "value3", Nombre = "Value4", Selected = true });
GridItems.Add(new GridItem { Codigo = "value5", Nombre = "Value6", Selected = false });
dgServicios.ItemsSource = GridItems;
}
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
}
}
public class GridItem : INotifyPropertyChanged
{
private bool _Selected;
public bool Selected
{
get { return _Selected; }
set
{
_Selected = value;
OnPropertyChanged();
}
}
private string _Codigo;
public string Codigo
{
get { return _Codigo; }
set
{
_Codigo = value;
OnPropertyChanged();
}
}
private string _Nombre;
public string Nombre
{
get { return _Nombre; }
set
{
_Nombre = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
If you provide more specific info on this, I might be able to give you a better answer.
I'm new to WPF and want to do some basic databinding.
I have a List of a CustomObject and want to bind it to a DataGrid.
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<ArticleItem> list = new List<ArticleItem>()
{
new ArticleItem(){ ID=3, Title="test", ViewCount=5},
new ArticleItem(){ ID=3, Title="test", ViewCount=5},
new ArticleItem(){ ID=3, Title="test", ViewCount=5},
new ArticleItem(){ ID=3, Title="test", ViewCount=5},
};
}
}
public class ArticleItem
{
public int ID { get; set; }
public int ViewCount { get; set; }
public String Title { get; set; }
}
}
This is how my grid looks like:
<DataGrid Height="179" HorizontalAlignment="Left" Margin="54,65,0,0" Name="dataGrid1" VerticalAlignment="Top" Width="382">
<DataGrid.Columns>
<DataGridTextColumn Header="ID"/>
<DataGridTextColumn Header="ViewCount" />
<DataGridTextColumn Header="Title" />
</DataGrid.Columns>
</DataGrid>
I'm used to the databinding from ASP.Net, where I can easily say:
this.dataGrid1.DataSource = list;
How must I proceed in WPF?
if you do not expect that your list will be recreated then you can use the same approach as you've used for Asp.Net (instead of DataSource this property in WPF is usually named ItemsSource):
this.dataGrid1.ItemsSource = list;
But if you would like to replace your list with new collection instance then you should consider using databinding.
You should do it in the xaml code:
<DataGrid ItemsSource="{Binding list}" [...]>
[...]
</DataGrid>
I would advise you to use an ObservableCollection as your backing collection, as that would propagate changes to the datagrid, as it implements INotifyCollectionChanged.
Actually, to properly support sorting, filtering, etc. a CollectionViewSource should be used as a link between the DataGrid and the list, like this:
<Window.Resources>
<CollectionViewSource x:Key="ItemCollectionViewSource" CollectionViewType="ListCollectionView"/>
</Window.Resources>
The DataGrid line looks like this:
<DataGrid
DataContext="{StaticResource ItemCollectionViewSource}"
ItemsSource="{Binding}"
AutoGenerateColumns="False">
In the code behind, you link CollectionViewSource with your link.
CollectionViewSource itemCollectionViewSource;
itemCollectionViewSource = (CollectionViewSource)(FindResource("ItemCollectionViewSource"));
itemCollectionViewSource.Source = itemList;
For detailed example see my article on CoedProject:
http://www.codeproject.com/Articles/683429/Guide-to-WPF-DataGrid-formatting-using-bindings
You dont need to give column names manually in xaml. Just set AutoGenerateColumns property to true and your list will be automatically binded to DataGrid. refer code.
XAML Code:
<Grid>
<DataGrid x:Name="MyDatagrid" AutoGenerateColumns="True" Height="447" HorizontalAlignment="Left" Margin="20,85,0,0" VerticalAlignment="Top" Width="799" ItemsSource="{Binding Path=ListTest, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" CanUserAddRows="False"> </Grid>
C#
Public Class Test
{
public string m_field1_Test{get;set;}
public string m_field2_Test { get; set; }
public Test()
{
m_field1_Test = "field1";
m_field2_Test = "field2";
}
public MainWindow()
{
listTest = new List<Test>();
for (int i = 0; i < 10; i++)
{
obj = new Test();
listTest.Add(obj);
}
this.MyDatagrid.ItemsSource = ListTest;
InitializeComponent();
}