GridView in WPF - How to use it? - c#

Ive looked about 50 or more webpages about this control. and it seems to me that it might be useless to me.
It seams that the "GridView" is a "View" of the "ListView" control.
i dont have any issues in using the control and loading data but manipulating it seems to be difficult.
public partial class Designer : UserControl
{
public Designer()
{
InitializeComponent();
List<RandomData> NewItem = new List<RandomData>();
NewItem.Add(new RandomData { Column1 = "Item1", Column2 = "MoreData", Column3 = "MoreData", Column4 = "MoreData" });
ListView_1.ItemsSource = NewItem;
}
}
public class RandomData
{
public String Column1 { get; set; }
public String Column2 { get; set; }
public String Column3 { get; set; }
public String Column4 { get; set; }
}
Simple enough, it loads the data into the columns.
however what it i want to load other stuff in there. like a checkbox or a image file or something.
public Designer()
{
InitializeComponent();
CheckBox AttemptThis = new CheckBox();
AttemptThis.Content = "Testing";
AttemptThis.IsChecked = true;
List<RandomData> NewItem = new List<RandomData>();
NewItem.Add(new RandomData { Column1 = AttemptThis, Column2 = "MoreData", Column3 = "MoreData", Column4 = "MoreData" });
ListView_1.ItemsSource = NewItem;
}
}
public class RandomData
{
public CheckBox Column1 { get; set; }
public String Column2 { get; set; }
public String Column3 { get; set; }
public String Column4 { get; set; }
}
And i get the checkbox.tostring() appear in the column??
is this control going to be able to do this?
Also is there a way to have a checkbox or image appear if the cell is a certian value ?

If you're working with WPF, you really need to forget any and all notions and approaches you might be used to from archaic technologies such as winforms and understand and embrace The WPF Mentality.
manipulating it seems to be difficult
YES. The WPF Visual Tree is a really complex structure with all sorts of arcane behavior that you really do not want to get into. That's why you must Learn MVVM before you ever write a single line of code in WPF
I want to load other stuff in there. like a checkbox or a image file
or something.
You do not "load" stuff into the UI. You define the UI in XAML and use DataBinding to bind the UI to relevant data, defined in either a Data Model or a ViewModel:
<Window x:Class="WpfApplication7.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<ListView ItemsSource="{Binding}">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected}"
Content="{Binding DisplayName}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Window>
Code Behind:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
DataContext = Enumerable.Range(0,10)
.Select(x => new DataItem()
{
DisplayName = "Item" + x.ToString(),
IsSelected = x % 2 == 0
});
}
}
Data Item:
public class DataItem
{
public bool IsSelected { get; set; }
public string DisplayName { get; set; }
}
Result:
See how there is no need at all to manipulate any UI element in procedural code. You simply define the Data in the form of simple properties and then set the UI's DataContext to that.
Two-Way DataBinding will also save you the need to "read the values" back from the UI after they've been modified. There is no such thing as "read data from the UI" in WPF because DataBinding takes care of that. Simply read the data from your Data Items.
WPF Rocks. Copy and paste my code in a File -> New Project -> WPF Application and see the results for yourself.

Related

How to assign a List of Booleans to a generated Checkbox IsChecked property in WPF?

I've recently started learning C# and I've encountered a problem. I display a set of keywords in a generated checkbox in my WPF and I want to check the element (IsChecked) based on an input check from a TXT file.
If the currently selected element from a different listbox matches the read modelclass(from a txt file) then set the checked key true.
I'm generating a Checkbox in my WPF to list a set of keywords that my app reads from a txt file.
The txt file contains the following items per line:
-id
-key
-pair
-description
WPF code:
<ListView ItemsSource="{Binding XAMLModelKeywords}" SelectedItem="{Binding XAMLModelKeyword}" Margin="5" x:Name="listofallkeys" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" >
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding XAMLAssignedKeys}" Content="{Binding Key}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
C#:
public ModelTemplates XAMLModelTemplate { get; set; }
public ModelKeywords XAMLModelKeyword { get; set; }
public List<bool> XAMLAssignedKeys { get; set; }
public string XAMLKeyword { get; set; }
public ViewModelMain()
{
//This creates a new instance of ObservableCollection above
XAMLModelTemplates = new ObservableCollection<ModelTemplates>();
XAMLModelKeywords = new ObservableCollection<ModelKeywords>();
XAMLAssignedKeys = new List<bool>();
Refresh();
}
public void Refresh()
{
XAMLModelTemplates.Clear();
foreach (ModelTemplates tpl in ReadInput.ReadTemplateDirectory(Path))
{
XAMLModelTemplates.Add(tpl);
}
//Selecting the first item from the returned list
XAMLModelTemplate = XAMLModelTemplates.FirstOrDefault();
XAMLModelKeywords.Clear();
foreach (ModelKeywords tpl in ReadInput.ReadKeywordsFile(KeyWordsPath))
{
XAMLModelKeywords.Add(tpl);
}
XAMLModelKeyword = XAMLModelKeywords.FirstOrDefault();
XAMLAssignedKeys.Clear();
foreach (ModelKeywords tpl in XAMLModelKeywords)
{
XAMLAssignedKeys.Add(ReadInput.CheckPairedtemplates(tpl, XAMLModelTemplate));
}
ModelKeyword:
public class ModelKeywords
{
public int Id { get; set; }
public string Key { get; set; }
public List<string> PairedTemplates { get; set; }
public string Description { get; set; }
}
ModelTemplate:
public class ModelTemplates
{
//path to a template
public int Id { get; set; }
public string TemplatePath { get; set; }
public string TemplateName { get; set; }
public ExcelRange TemplateRange { get; set; }
}
ReadKeywordsFile:
Returns a list of Template Models (name of template file, path) and the displays it in a listbox.
ReadKeywordsFile:
Returns a list of Keywords Model (id, key, pair, desc) and then displays it in a generated listbox.
CheckPairedtemplates:
Returns a list of booleans based on the currently selected Template Model matches the Keywords Model pair (list of string).
TLDR:
I have a list of booleans ( XAMLAssignedKeys) and I want to match it to my generated checkbox in WPF, however the generation happens based on an item template and I'm not sure how to link one element from my list of booleans to the checkbox "IsChecked" property.
ScreenshotofApp
Thank you much in advance for the advices.
Since you set ObservableCollection<ModelKeywords> (XAMLModelKeywords) to ItemsSource property of ListView (listofallkeys), each item of the ListView will be bound to a member of the ObservableCollection. In this case, the CheckBox inside DataTemplate will be bound to a ModelKeywords in XAMLModelKeywords. Therefore, you need to bind the properties of CheckBox with the properties of ModelKeywords.
In the Xaml, you mistakenly set XAMLAssignedKeys to IsChecked property of CheckBox. It must be a property of ModelKeywords. The remedy will be to add a bool property to ModelKeywords, somehow copy a value in the XAMLAssignedKeys to the property, and then bind the property with IsChecked properties of CheckBox.
Let's say, if you add IsEnabled bool property to ModelKeywords, the Xaml will be
<CheckBox IsChecked="{Binding IsEnabled}" Content="{Binding Key}"/>
In addition, unless you implement INotifyPropertyChanged interface to ModelKeywords, only initial value of the property will be sent to the CheckBox and the subsequent values will not be notified and reflected to the CheckBox when the property is changed.

Seperate object per column in a datagrid row

I have a datagrid from SCADA software that I am recreating in WPF by creating a custom user control that can be used for more use cases. I have an observable collection of type SettingRow that contains the data of one cell. The problem is that it is not containing the data of one cell.
If I have 10 items in my collection than I cannot see an seperate item in for example Row 1, Column 3. I see the same item in for example Row 1, Column 2. So in short: I see the same SettingRow object no matter what column I select in a row.
Every cell is an seperate setting object that is bound to an DataTemplateSelector to select the appropiate datatemplate to display the value (boolean as checkbox, string and value as textbox etc.)
I have created a DataGrid that has a templateselector based on the value in the SettingRow class. The datagrid's itemssource is currently bound to the ObservableCollection in my viewmodel. It works, I see every SettingRow in each cell per row, but I am quite sure that I am seeing the same SettingRow in each column which is not an seperate object that is independent of the object in the column before that one.
I've tried some solutions on StackOverflow with an observable collection inside an observable collection but I do not really get it to work.
Underneath is the class that is displayed in each row. Does this object get copied over to the next column? Or is the same object re-used in every column?
public class SettingRow : ObservableObject
{
public int ID { get; set; }
public object Value { get; set; }
public string Name { get; set; }
public string Unit { get; set; }
}
My datagrid in XAML is declared and only has the Name property. The rest are added through the code.
<DataGrid x:Name="OverviewDataGridView" ItemsSource="{Binding SettingsCollection, Mode=TwoWay}" AutoGenerateColumns="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}">
</DataGridTextColumn>
</DataGrid.Columns>
Below I am creating a column per phase. The 4 phases are simulated since I do not have actual data. Now, every SettingRow should be individually editable and I need to retrieve it from the ViewModel.
This is in the code behind.
public void GenerateColumns()
{
//Simulate 4 phases and adding a column per phase!
for(int i = 0; i < 4; i++)
{
DataGridTemplateColumn templateCol = new DataGridTemplateColumn();
templateCol.Header = "Phase " + i;
TemplateSelector templateSel = new TemplateSelector();
templateSel.BooleanTemplate = this.Resources["BooleanTemplate"] as DataTemplate;
templateSel.TextTemplate = this.Resources["TextTemplate"] as DataTemplate;
templateCol.CellTemplateSelector = templateSel;
this.OverviewDataGridView.Columns.Add(templateCol);
}
}
The ViewModel is quite simple it creates some test data. Every SettingRow that is created is displayed in every column, but I think I see the same object in each
public class DynamicDataGridViewModel : ObservableObject
{
private ObservableCollection<SettingRow> settingsCollection;
public ObservableCollection<SettingRow> SettingsCollection
{
get { return settingsCollection; }
set
{
settingsCollection = value;
RaisePropertyChangedEvent("SettingsCollection");
}
}
public DynamicDataGridViewModel()
{
SettingsCollection = new ObservableCollection<SettingRow>();
SettingsCollection.Add(new SettingRow() { ID = 0, Name = "Phase Active", Value = true, Unit = "" });
SettingsCollection.Add(new SettingRow() { ID = 1, Name = "Minimum phase duration", Value = +900, Unit = "s" });
SettingsCollection.Add(new SettingRow() { ID = 2, Name = "Min Supply air temperature", Value = 50.0302, Unit = "C" });
}
}
And lastly, my DataTemplateSelector. It only handles the value of the SettingRow and display the correct symbol (boolean = checkbox etc.)
public class TemplateSelector : DataTemplateSelector
{
public DataTemplate BooleanTemplate { get; set; }
public DataTemplate TextTemplate { get; set; }
public SettingRow Content { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
ContentPresenter presenter = container as ContentPresenter;
DataGridCell cell = presenter.Parent as DataGridCell;
SettingRow row = (cell.DataContext as SettingRow);
if(row != null && row.Value != null)
{
this.Content = row;
switch (Type.GetTypeCode(row.Value.GetType()))
{
case TypeCode.Boolean:
return BooleanTemplate;
default:
return TextTemplate;
}
}
else
{
return null;
}
}
}
EDIT:
Templates for the columns below
<DataTemplate x:Key="BooleanTemplate">
<StackPanel Orientation="Horizontal" Margin="2">
<CheckBox IsChecked="{Binding Value}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="TextTemplate">
<StackPanel Orientation="Horizontal" Margin="2">
<TextBox Height="25" MinWidth="70" FontSize="14">
<TextBox.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="Value" Mode="TwoWay"/>
<Binding Path="Unit" Mode="TwoWay"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
</StackPanel>
</DataTemplate>
What I need is a DataGrid bound to an observable collection. Which has dynamic amount of columns (represents an phase) and I need one SettingRow in every column and it to be editable per object. So that I can for example set the visibility of the Phase Duration setting in the Levelling phase to invisible.
EDIT 2:
What I have in mind is to use a observable collection inside an observable collection like the snippet below this way I can represent my data as the columns in each row. This way I can access every row and column individual object.:
private ObservableCollection<ObservableCollection<SettingRow>> items;
public ObservableCollection<ObservableCollection<SettingRow>> ItemsSourceOfDataGrid
{
get { return items; }
set
{
items = value;
RaisePropertyChangedEvent("ItemsSourceOfDataGrid")
}
}
EDIT: So after looking over the comments and what you want, the EASIEST thing to do is to define all of your row names and then have the ObservableCollection be a collection of columns instead of rows. If you want to go the crazy route of both dynamic rows and columns, well these can get you started:
https://svitla.com/blog/grid-with-dynamic-number-of-rows-and-columns-part-1
https://svitla.com/blog/grid-with-dynamic-number-of-rows-and-columns-part-2
The source Github: https://github.com/IReznykov/Blog
So, the way to do it with dynamic columns instead is pretty straight forward.
Just to reiterate:
Make sure your RaisePropertyChangedEvent is added to the items in your model. This will allow them to be changed dynamically inside your SettingsCollection. Also, the XAML portion of your DataGrid needs to be setup in a certain way to work with this.
public class SettingColumn : ObservableObject
{
public int _PhaseNumber;
public int PhaseNumber
{
get { return _PhaseNumber; }
set
{
PhaseNumber = value;
RaisePropertyChangedEvent("PhaseNumber");
}
}
public object _MinPhaseDuration;
public object MinPhaseDuration
{
get { return _Value; }
set
{
Value = value;
RaisePropertyChangedEvent("Value");
}
}
public string _MinSupplyAirTemp;
public string MinSupplyAirTemp
{
get { return _MinSupplyAirTemp; }
set
{
MinSupplyAirTemp = value;
RaisePropertyChangedEvent("MinSupplyAirTemp");
}
}
// The rest of your row labels here.
.
.
.
Hope this edit helps get you going. Again, dynamic rows AND columns is a pain. Save yourself some headache and define either all your row labels or your column labels.

WPF Combobox display only SOME items

So I have a WPF app(with MVVM) and in this I have a combobox which binds to a table in my database and displays the values, this works just fine.
However, now I want to make a new combobox and bind it to the same table, but now I only want it to display SOME of the values. Is there a simple way to do this?
The table has has four entries but I only want to show 3 of them in this new combobox.
I know I could just make a new table in the database to bind to, but I might have to use several of these comboboxes(with different values) and I'd rather not go through all that bother if I can avoid it.
XAML:
<ComboBox
Name="cmComp"
MinWidth="150"
Margin="12 0 0 12"
ItemsSource="{Binding SelectedComponentLookup}"
DisplayMemberPath="ComponentChoice"
SelectedValuePath="ComponentChoice"
SelectedItem="{Binding ComponentChosen}">
</ComboBox>
VIEWMODEL:
private IEnumerable<ComponentLookupDto> _selectedComponentLookup;
public IEnumerable<ComponentLookupDto> SelectedComponentLookup
{
get { return _selectedComponentLookup; }
set
{
_selectedComponentLookup = value;
}
}
DTO:
public class ComponentLookupDto
{
public int ComponentLookupId { get; set; }
public string ComponentChoice { get; set; }
}
The way I achieve this is that I filter out the items I don't want to display in the getter for the property to which I bind my ItemsSource. :
XAML:
<ComboBox ItemsSource={Binding SelectedComponentLookupOther} ... />
And in your ViewModel:
public IEnumerable<ComponentLookupDto> SelectedComponentLookupOther
{
get { return _selectedComponentLookup.Where(c => c.SomeProperty == "however you want to pick it out"); }
}

How to delete a ListView Item by a function in UserControl Template?

I am Developing windows 10 Universal app with c#.
I have a UserControl that is the MyListview item template. Listview will Bind the data. In userControl,there is a button for Delete the usercontrol DependencyProperty Content(contain string Text, Name and int Id ).
Listview show the text of object and the button for remove it.
Now how can remove that item from my List by click on remove Button?
Update
my Data class:
class Data
{
public int Id { get; set; }
public string Text { get; set; }
}
my usercontrol.cs :
public Data Content
{
get { return (Data)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
// Using a DependencyProperty as the backing store for Content. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register("Content", typeof(Data), typeof(MyUserControl1), new PropertyMetadata(null));
usercontrol xaml:
<StackPanel>
<TextBlock x:Name="textBlock" Text="{Binding Content.Text, ElementName=textBlock}" />
<Button Click="Remove_Click"/>
</StackPanel>
my list implementing:
<Page.Resources>
<DataTemplate x:Key="ListViewTemplate">
<local:MyUserControl1 Content="{Binding}"/>
</DataTemplate>
</Page.Resources>
<Grid>
<ListView x:Name="ListView" ItemTemplate="{StaticResource ListViewTemplate}" />
</Grid>
and in the code behinde the Page I use an ObservableCollection<Data> items = new ObservableCollection<Data>();to set Listview.ItemsSource to it.
The main Problem is How to remove that item from the items in MyUsercontrol1
You wrote about binding so I'm assuming that in your XAML there is a following code or similar:
<ListView ItemSource = "{Bind SomeCollection"} ... />
If I'm right there is no much to do. If SomeCollection is of type ObservableCollection<T> it is enough to remove an item from SomeCollection and UI will be refreshed ''automatically''. To sum up:
Declare SomeCollection as ObservableCollection<T>.
In a command that is executed when Delete button is clicked (or in an event handler) simply call ObservableCollection<T>.Remove.
UPDATE
This code is not elegant but shows an idea. Firstly we need to modify Data class:
public class Data
{
public int Id { get; set; }
public string Text { get; set; }
public Action<Data> OnRemoveCallback { get; set; }
public void OnRemove()
{
OnRemoveCallback(this);
}
}
OnRemoveCallback will be used to inform ListView that a given data element should be removed. Remove_click handler in MyUserControl simply executes OnRemove:
private void Remove_Click(object sender, RoutedEventArgs e)
{
Content.OnRemove();
}
Finally, in the code behind of your Page we have to define a logic that will be responsible for actual removing data items from the list:
public void Remove(Data d)
{
((ObservableCollection<Data>) ListView.ItemsSource).Remove(d);
}
...
ListView.ItemsSource = new ObservableCollection<Data>()
{
new Data() {Id = 1, Text = "1", OnRemoveCallback = Remove},
new Data() {Id = 2, Text = "2", OnRemoveCallback = Remove}
};
Now your Page will be informed whenever Delete button is pressed and will do a job.
As I said it is not a perfect solution. Personally, I'll use MVVM pattern. Thanks do that XAML and C# will be seperated.

Pass variables to text boxes in C# WP8

I have a Pivotelement, so a Windows Phone Site with multiple sites and on every site should stand 5 menus. I have a List in which there every 5 menus and each menu of each site must be in a textbox:
//Site 1
textbox1_site1.text = list[0].menu1;
textbox2_site1.text = list[0].menu2;
...
//Site 2
textbox1_site2.text = list[1].menu1;
textbox2_site2.text = list[1].menu2;
...
//Site 3
textbox1_site3.text = list[2].menu1;
...
Here is where my list comes from.
public class Menus
{
public date Datum { get; set; }
public string menu1 { get; set; }
public string menu2 { get; set; }
...
}
public class list
{
public List<Menus> list { get; set; }
}
And I don't get it. how can I shorten this up in a loop like:
for(int i=0; i<5; i++)
{
textbox[i+1]_site[i+1] = list[i].menu1;
...
}
I know it's a beginner question and it's a little bit dumb but I don't get it.
from what i understand you have a list of values and want to display each value in a textbox. you can do this with a little data binding. You need to set the ItemsSource property of an ItemsControl to your data source (the list of values). After this all you have to do is modify the ItemsControl.ItemTemplate property. See if the code below helps. As you can see, everything is in the code-behind to keep it short.
//in ctor, after InitializeComponents()
DataContext = this;
//create the source of data (values to display in text boxes)
public List<string> Data{
get{
return new List<string>(){"item1", "item2"};
}
}
the rest is in the xaml file. Create an ItemsControl the gets the data from the Data property and displays it.
<ItemsControl ItemsSource="{Binding Data}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Mode=TwoWay}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
this should solve your problem

Categories

Resources