I am trying to populate my List View which is basically a comparison between 2 folders.
I am using WPF List View :
XAML :
<ListView x:Name="listView" Margin="0,39,0,0">
<ListView.View>
<GridView x:Name="gridView"/>
</ListView.View>
</ListView>
MainWindow :
private void compare_Click(object sender, RoutedEventArgs e)
{
.
.
.
ListHelper listHelper = new ListHelper(listView, gridView);
CompareResults = results.CoalescedResults();
listHelper.AddItems(CompareResults);
}
ListHelper Class
class ListHelper
{
public ListView listView { get; set; }
public GridView gridView { get; set; }
public ListHelper (ListView list, GridView grid)
{
this.listView = list;
this.gridView = grid;
listView.View = gridView;
InitializeList();
}
public void InitializeList()
{
listView.Items.Clear();
// Add columns
addColumns("File/Folder Name");
addColumns("Left Folder");
addColumns("Right Folder");
addColumns("Match");
}
public void addColumns(string colName)
{
gridView.Columns.Add(new GridViewColumn
{
Header = colName,
DisplayMemberBinding = new Binding(colName),
Width = 30
});
}
public void AddItems(List<CompareResult> compareResults)
{
foreach (var item in compareResults)
{
ListDataRow row = new ListDataRow()
{
TypeName = item.GetFileOrFolderName(),
LeftFolder = item.LeftFilePath != string.Empty ? Path.GetDirectoryName(item.LeftFilePath) : string.Empty,
RightFolder = item.RightFilePath != string.Empty ? Path.GetDirectoryName(item.RightFilePath) : string.Empty,
MatchStatus = "MATCH STATUS TEMP"
};
listView.Items.Add(row);
}
}
ListDataRow
public class ListDataRow
{
public string TypeName { get; set; }
public string LeftFolder { get; set; }
public string RightFolder { get; set; }
public string MatchStatus { get; set; }
}
Now on adding breakpoints, I can see that valid data in being inserted in listView Add items call..
But on execution, only columns are visible, no data items.
Thanks everyone,
I figured out the error.
Please match the Names of Columns with the object (ListDataRow in above example)
Related
I have been reading documentation for several days now but I can't get it working, no matter what I try. I have Basic Row chart and want to display as a graph time spent. My bar title and value are changing constantly (more items getting added). I am able to add bars with my current code, but I am not able to add title for each added bar. Only first title / first bar title is visible, all the others / coming are not visible.
How to add title and value in a proper way? (I am already familiar with documentation https://lvcharts.net/App/examples/v1/wf/Basic%20Row)
Here is my code (you can see from commented out sections what has been tried yet):
public static SeriesCollection SeriesCollection { get; set; }
public static string[] Labels { get; set; }
public static List<string> LabelsList { get; set; }
public static Func<double, string> Formatter { get; set; }
public AppUsageBarGraph()
{
InitializeComponent();
LabelsList = new List<string>();
SeriesCollection = new SeriesCollection
{
new RowSeries
{
Values = new ChartValues<double> { },
DataLabels = true
}
};
DataContext = this;
}
public static void UpdateChart()
{
SeriesCollection[0].Values.Clear();
LabelsList.Clear();
//Labels = MainProcess.ActivityLogGrouped.Rows.Cast<DataRow>().Select(row => row["Window Title"].ToString()).ToArray();
foreach (DataRow row in MainProcess.ActivityLogGrouped.Rows)
{
SeriesCollection[0].Values.Add(Convert.ToDouble(row["Time Spent"]));
//SeriesCollection[0]. = row["Time Spent"].ToString());
LabelsList.Add(row["Window Title"].ToString());
}
//MessageBox.Show(Labels[0].ToString());
Labels = LabelsList.ToArray();
//foreach (var item in Labels)
//{
// MessageBox.Show(item);
//}
//Labels = new[]
// {
// "Shea Ferriera",
// "Maurita Powel",
// "Scottie Brogdon",
// "Teresa Kerman",
// "Nell Venuti",
// "Anibal Brothers",
// "Anderson Dillman"
// };
//Formatter = value => value.ToString("N");
}
The key is to use a ObservableCollection<string> instead of a string[].
I also recommend to use a model to encapsulate the actual chart data points. I introduced the class DataModel for this reason.
The following example shows how to dynamically bind values and labels to the chart. I should say that making everything public static is a very bad smelling code design.
MainWindow.xaml
<Window>
<Window.DataContext>
<ViewModel />
</Window.DataContext>
<wpf:CartesianChart Height="500">
<CartesianChart.Series>
<RowSeries Values="{Binding ChartModel.RowSeries}"
Configuration="{Binding ChartModel.RowSeriesConfiguration}"/>
</CartesianChart.Series>
<CartesianChart.AxisY>
<Axis Labels="{Binding ChartModel.RowSeriesLabels}" />
</CartesianChart.AxisY>
</CartesianChart>
</Window>
ViewModel.cs
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
this.ChartModel = new ChartModel();
}
public void UpdateChart()
{
foreach (DataRow row in MainProcess.ActivityLogGrouped.Rows)
{
if (double.TryParse(row["Time Spent"], out double value)
{
string label = row["Window Title"];
var newDataModel = new DataModel(value, label);
this.ChartModel.RowSeries.Add(newDataModel);
this.ChartModel.RowSeriesLabels.Add(newDataModel.Label);
}
}
}
public ChartModel ChartModel { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
ChartModel.cs
public class ChartModel : INotifyPropertyChanged
{
public ChartModel()
{
// Initialize chart
this.RowSeries = new ChartValues<DataModel>()
{
new DataModel(20, "Shea Ferriera"),
new DataModel(100, "Maurita Powel"),
new DataModel(60, "Scottie Brogdon"),
};
// Create labels
this.RowSeriesLabels = new ObservableCollection<string>();
foreach (DataModel dataModel in this.RowSeries)
{
this.RowSeriesLabels.Add("dataModel.Label");
}
// DatModel to value mapping
this.RowSeriesConfiguration = new CartesianMapper<DataModel>()
.X(dataModel => dataModel.Value);
}
public CartesianMapper<DataModel> RowSeriesConfiguration { get; set; }
public ChartValues<DataModel> RowSeries { get; set; }
public ObservableCollection<string> RowSeriesLabels { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
DataModel.cs
public class DataModel
{
public DataModel(double value, string label)
{
this.Value = value;
this.Label = label;
}
public double Value { get; set; }
public string Label { get; set; }
}
I created a custom picker with the help of Lucas Zhang which you can check in the link
xamarin custom multiple picker
Now I have another question with this problem. When user select a group or groups, I need access to these selected parameters.
public class Grup
{
public int ID { get; set; }
public string GroupName { get; set; }
public Nullable<int> SubsID { get; set; }
}
This is the model I use. Picker reads Groupnames through ViewModel which is shown below.
public class NewUserViewModel
{
public List<Grup> GroupList { get; set; }
public List<Grup> SelectedGroup { get; set; }
}
And I want save these parameters which came from every pickers in the view to here and furthermore I will send them to database through API.Question is how can I access these IDs when user select them and click save button.
An easy way to do this is to listen for picker's selection events and then get the result of each selection by iterating over the pickerStack
like this(base on Lucas Zhang's sample):
in PickerView :
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class PickerView : ContentView
{
public Group SelectGroup; // add the SelectGroup property which save the result after you select
public ObservableCollection<Group> pickerSource { get; set; }
public PickerView(ObservableCollection<Group> source) //here i change the source to your Group model
{
InitializeComponent();
picker.ItemsSource = source;
}
private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
var stack = this.Parent as StackLayout;
stack.Children.Remove(this);
}
private void picker_SelectedIndexChanged(object sender, EventArgs e)
{
var picker = sender as Picker;
SelectGroup = (Group)picker.SelectedItem;
}
}
in PickerView.xaml just add the SelectedIndexChanged event:
<Picker Grid.Column="0" x:Name="picker" Title="{Binding GroupName}" ItemDisplayBinding="{Binding ID}" TitleColor="Red" SelectedIndexChanged="picker_SelectedIndexChanged" />
in your page :
public partial class MutiPicker : ContentPage
{
public MutiPicker()
{
InitializeComponent();
}
private void Button_Clicked(object sender, EventArgs e)
{
var source = new ObservableCollection<Group>() { new Group() { ID=111,GroupName="AAA",SubsID=1}, new Group() { ID = 222, GroupName = "BBB", SubsID = 2 }, new Group() { ID = 333, GroupName = "CCC", SubsID = 3 } };
pickerStack.Children.Add(new PickerView(source));
}
//iterate over your pickerviews
private void Update_Clicker(object sender, EventArgs e)
{
foreach (var pickerview in pickerStack.Children)
{
if (pickerview is PickerView && ((PickerView)pickerview).SelectGroup != null)
{
var selectgroup = ((PickerView)pickerview).SelectGroup;//here you will get your select group,then you could get its ID ,GroupName or SubsID
}
}
}
}
I've got a simple application connected to a SQL Server database, and I'm trying to pull data from the server to add it to a WPF ListView. I've done what I thought was the hard part of that, and successfully pulled the data - confirmed that already. But when I try to add it to my existing listview, the listview remains blank. Here's the code I've got to add it.
GetEmployees uses a middle tier class to connect to the database and retrieve the properties of an Employee. It successfully creates all of these variables and assigns them values from the database. The bottom line - lvwEmpSearch.Items.Add(emp); is what does not work.
Edit: Code is adjusted.
Code for adding to the listview:
public ObservableCollection<EmployeeViewModel> Employees { get; set; } = new ObservableCollection<EmployeeViewModel>();
private void btnFindAllEmployees_Click(object sender, RoutedEventArgs e)
{
List<Employee> empList = GetEmployees();
foreach (Employee emp in empList)
{
var model = new EmployeeViewModel
{
empID = emp._empID,
Name = emp._fName + " " + emp._lName,
Address = emp._address + ", " + emp._city + ", " + emp._state + " " + emp._zip,
HireDate = emp._doH,
Phone = emp._phone,
PayRate = emp._payRate,
Email = emp._email,
};
Employees.Add(model);
}
}
EmployeeViewModel class:
public class EmployeeViewModel
{
public int empID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public DateTime HireDate { get; set; }
public string Phone { get; set; }
public decimal PayRate { get; set; }
public string Email { get; set; }
}
Create a ViewModel to hold the item information you want displayed.
Based on column binding you are looking for something like
public class EmployeeViewModel {
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public DateTime HireDate { get; set; }
public string Phone { get; set; }
public decimal PayRate { get; set; }
public string Email { get; set; }
}
Create an observable collection to hold the items
public ObservableCollection<EmployeeViewModel> Employees { get; set; } = new ObservableCollection<EmployeeViewModel>();
and to allow the view to bind to the list
<ListView x:Name="lvwEmpSearch" ItemsSource="{Binding Employees}" >
<!-- ...removed for brevity -->
</ListView>
Now all that is needed is to populate the collection within the main ViewModel
List<Employee> empList = GetEmployees();
foreach (Employee emp in empList) {
var model = new EmployeeViewModel {
ID = emp._empID,
Name = emp._fName + " " + emp._lName,
Address = emp._address + ", " + emp._city + ", " + emp._state + " " + emp._zip,
HireDate = emp._doH,
Phone = emp._phone,
PayRate = emp._payRate,
Email = emp._email,
};
Employees.Add(model);
}
You seem to be doing everything in the code behind so you would need to bind the view. The initial assumption was that you were following the MVVM pattern.
//CTOR
public EmployeesView() {
this.InitializeComponents();
this.Employees = new ObservableCollection<EmployeeViewModel>();
//Bind the view so that
this.DataContext = this;
}
public ObservableCollection<EmployeeViewModel> Employees { get; private set; }
private void btnFindAllEmployees_Click(object sender, RoutedEventArgs e) {
//...code removed for brevity
}
Try doing the following:
In your view:
<ListView ItemsSource="{Binding Employees}">
<ListView.View>
<GridView>
<GridViewColumn Header="ID" DisplayMemberBinding="{Binding ID}"></GridViewColumn>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"></GridViewColumn>
</GridView>
</ListView.View>
</ListView>
In your code behind of the view:
public MainWindow() {
InitializeComponent();
DataContext = new MainViewModel();
}
The MainViewModel code:
public class MainViewModel : INotifyPropertyChanged {
public MainViewModel() {
FEmployees = new ObservableCollection<Employee>();
FEmployees.Add(new Employee {ID = 1,Name="Jordy van Eijk"});
FEmployees.Add(new Employee {ID = 2,Name="John Doe"});
FEmployees.Add(new Employee {ID = 3,Name="Jane Doe"});
}
private ObservableCollection<Employee> FEmployees;
public ObservableCollection<Employee> Employees {
get { return FEmployees; }
set {
FEmployees = value;
OnPropertyChanged(nameof(Employees));
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string APropertyName = null) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(APropertyName));
}
}
public class Employee {
public int ID { get; set; }
public string Name { get; set; }
}
Youshould be able to create an ObservableCollection containing your employees and bind them to the ItemsSource.
private ObservableCollection<Employee> _employees = new ObservableCollection<Employee>();
public ObservableCollection<Employee> Employees
{
get { return _employees; }
protected set
{
if (_employees == value)
return;
_employees = value;
OnPropertyChanged("Employees");
}
}
And bind it like this:
<ListView ItemsSource="{Binding Employees}"/>
And in code you could fill it with:
Employees.AddRange(GetEmployees());
I'm creating a form that will allows user to add filters to data for processing.
I have setup:
public Class RuleGroup
{
public ObservableCollection<Rule> Rules {get; set;}
...
}
public Class Rule
{
public ObservableCollection<String> Fields {get; set;}
public ObservableCollection<Rule> Rules {get; set;}
...
}
public class Criteria
{
public int ItemId{ get; set;}
public string Field{ get; set;}
public OperationType Operation{ get; set;}
public string Value {get; set;}
public string Value2 {get; set;}
}
So a Rule has a List of Criteria that must be matched if the rule is to be applied. Each Criteria in a Rule must specify a value for every field selected. The Amount of fields may vary from One RuleGroup to the next.
I am trying to set up a form that is user friendly when creating multiple Rules. I was thinking of having a GridView on the form that is some how bound to this class layout.
Rule = Row
Criteria = Column
Currently I have function that generates a DataTable based on the Rules/Criteria as the user move from one RuleGroup to the next, but I think there my be an nicer solution to this
Any ideas or help would be much appreciated
Thanks
Right Think I have got it.
Needed to change my Classes around a bit to get the correct groups / hierarchy. I have then been able to bind the column using the items index in the collection.
This has given me the outcome I wanted, Though there is a minor issue where I would like to be able to access the index using the string Name rather then the position. I am currently having to make sure that the "Criterion" are in the correct order when accessing the values.
Here is a link to the Source code
Rule Group
public class RuleGroup
{
public String Description { get; set; }
public ObservableCollection<Rule> Rules { get; set; }
public RuleGroup()
{
Rules = new ObservableCollection<Rule>();
}
}
Rule
public class Rule
{
public Rule()
{
Criteria = new ObservableCollection<Criteria>();
}
public String Description { get; set; }
public ObservableCollection<Criteria> Criteria { get; set; }
readonly ObservableCollection<RuleField> _Fields = new ObservableCollection<RuleField>();
public IEnumerable<RuleField> Fields
{
get
{
return _Fields;
}
}
public void AddField(string name, string header)
{
if (_Fields.FirstOrDefault(i => i.Name == name) == null)
{
RuleField field = new RuleField(_Fields.Count)
{
Name = name,
Header = header
};
_Fields.Add(field);
AddFieldToCriteria(field);
}
}
void AddFieldToCriteria(RuleField field)
{
foreach (Criteria c in Criteria)
{
if (c.Values.FirstOrDefault(i => i.Field == field) == null)
c.Values.Add(new Criterion() { Field = field, Operation = 1 });
}
}
}
Criteria
public class Criteria
{
public Criteria()
{
Values = new ObservableCollection<Criterion>();
}
public ObservableCollection<Criterion> Values { get; set; }
public Criterion this[int index]
{
get
{
return Values.OrderBy(i=>i.Field.Position).ElementAt(index);
}
set
{
Criterion c = Values.OrderBy(i => i.Field.Position).ElementAt(index);
c= value;
}
}
}
Criterion
public class Criterion
{
public RuleField Field { get; set; }
public int Operation { get; set; }
public object Value { get; set; }
public object Value2 { get; set; }
}
RuleField
public class RuleField
{
public string Name { get; set; }
public string Header { get; set; }
int _Position = 0;
public int Position
{
get
{
return _Position;
}
}
public RuleField(int position)
{
_Position = position;
}
}
View Model
public delegate void UpdateColumnsEventHandler(object sender, UpdateColumnsEventArgs e);
public class UpdateColumnsEventArgs
{
public IEnumerable<RuleField> Columns { get; set; }
public UpdateColumnsEventArgs()
{
Columns = new List<RuleField>();
}
public UpdateColumnsEventArgs(IEnumerable<RuleField> columns)
{
Columns = columns;
}
}
public class MainWindowViewModel
{
public event UpdateColumnsEventHandler UpdateColumns;
public ObservableCollection<RuleGroup> RuleGroups { get; set; }
RuleGroup _SelectedRuleGroup = null;
public RuleGroup SelectedRuleGroup
{
get
{
return _SelectedRuleGroup;
}
set
{
if (_SelectedRuleGroup == value)
return;
_SelectedRuleGroup = value;
}
}
public Rule _SelectedRule = null;
public Rule SelectedRule
{
get
{
return _SelectedRule;
}
set
{
if (_SelectedRule == value)
return;
_SelectedRule = value;
if (UpdateColumns != null)
UpdateColumns(this, new UpdateColumnsEventArgs(_SelectedRule.Fields));
}
}
public MainWindowViewModel()
{
RuleGroups = new ObservableCollection<RuleGroup>();
RuleGroup rg = new RuleGroup();
rg.Description = "Rule Group A";
Rule r = new Rule();
r.Description = "Rule 1";
Random random = new Random();
int range = 10000;
for (int x = 0; x < 2000; x++)
{
Criteria c = new Criteria();
c.Values.Add(new Criterion()
{
Field = new RuleField(0)
{
Name = "A",
Header = "A Column"
},
Operation = 1,
Value = "X"
});
c.Values.Add(new Criterion()
{
Field = new RuleField(0)
{
Name = "B",
Header = "B Column"
},
Operation = 1,
Value = x % 10
});
r.Criteria.Add(c);
}
#region Fields
r.AddField("A", "A Column");
r.AddField("B", "B Column");
r.AddField("C", "C Column");
#endregion
rg.Rules.Add(r);
r = new Rule();
r.Description = "Rule 2";
for (int x = 0; x < 1750; x++)
{
r.Criteria.Add(new Criteria());
}
#region Fields
r.AddField("A", "A Column");
r.AddField("B", "B Column");
#endregion
rg.Rules.Add(r);
RuleGroups.Add(rg);
}
}
WPF Window
<Window x:Class="RuleMappingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:RuleMappingTest"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<vm:MainWindowViewModel UpdateColumns="UpdateGridColumns"/>
</Window.DataContext>
<Grid Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<ListBox Grid.Row="0" ItemsSource="{Binding RuleGroups}" SelectedItem="{Binding SelectedRuleGroup}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Description}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Grid.Row="1" ItemsSource="{Binding SelectedRuleGroup.Rules}" SelectedItem="{Binding SelectedRule}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Description}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<DataGrid x:Name="CriteriaGrid" Grid.Row="2" ItemsSource="{Binding SelectedRule.Criteria}" AutoGenerateColumns="False" >
</DataGrid>
</Grid>
</Window>
WPF Code behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public void UpdateGridColumns(object sender, UpdateColumnsEventArgs e)
{
CriteriaGrid.Columns.Clear();
foreach (RuleField rf in e.Columns)
{
DataGridTextColumn c = new DataGridTextColumn();
c.Header = rf.Header;
Binding b = new Binding(String.Format("[{0}].Value", rf.Position));
CriteriaGrid.Columns.Add(c);
c.Binding = b;
}
}
}
I have a comboBox filed from a list thus:
locationCcomboBox.DataSource = ReadExcelFile(ExcelFilePath, "some properties"); \\ returns a list of class property.
locationCcomboBox.DisplayMember = "Location";
the Class is a simple class:
public string chain { get; set; }
public string location { get; set; }
public string postcode { get; set; }
public string phone { get; set; }
What I can't get into my head is how when the user selects an option from the combobox is how I select the phone,chain etc to write the correct value out to a text box for each!
BrainGoneSouth!
Handle the SelectedIndexChanged event of your locationCcomboBox an then get your class instance by the SelectedItem property:
//At form load or constructor:
locationCcomboBox.SelectedIndexChanged += locationCcomboBox_SelectedIndexChanged;
private void locationCcomboBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (locationCcomboBox.SelectedIndex > -1)
{
Class myClass = locationCcombo.SelectedItem as Class;
if (myClass != null)
{
//access the members of myClass here
}
}
}