i make a wpf application its running well.but whenever the size of my xml is to big its running very slow every time we fetch data from xml as code below
is any body suggest me it is because of this or may be other problem
how can i reform this
thanks
shashank`
DataSet xmlData = new DataSet();
XmlTextReader reader = null;
try
{
if (File.Exists(CommonUtils.xmlPath))
{
//convert XmlDocument to XmlTextReader
reader = new XmlTextReader(new StringReader(CommonUtils.DecryptXML().OuterXml));
//get the xml data
xmlData.ReadXml(reader);
reader.Close();
//get category rows from
DataRow[] eventRows = xmlData.Tables["M_EVENT"].Select(" ROW_STATUS=1");
if (eventRows.Length > 0)
{
//create a datatable for event
DataTable dtEvent = xmlData.Tables["M_EVENT"].Clone();
//add a default row to the event table
DataRow dr = dtEvent.NewRow();
dr[0] = "-1";
dr[1] = "--Select Event--";
dr[2] = "1";
dtEvent.Rows.InsertAt(dr, 0);
foreach (DataRow row in eventRows)
{
DataRow drEvent = dtEvent.NewRow();
drEvent["PK_EVENT_ID"] = row["PK_EVENT_ID"];
drEvent["EVENT_NAME"] = row["EVENT_NAME"];
drEvent["EVENT_TYPE"] = row["EVENT_TYPE"];
dtEvent.Rows.Add(drEvent);
}
//bind the category drop down
cmbEvent.DataContext = dtEvent.DefaultView;
cmbEvent.SelectedValuePath = "PK_EVENT_ID";
cmbEvent.DisplayMemberPath = "EVENT_NAME";
cmbEvent.SelectedIndex = 0;
}
}
else
{
Lblgetevent.Visibility = Visibility.Visible;
}
}`
Ouch!
What are you using DataTable for?! It is terribly inefficient for this purpose and requires you to write lots of extra code. Also, why are you setting ComboBox properties from code and not in XAML?
A much, much simpler way is to bind your ComboBox directly to the XML:
<ComboBox ItemsSource="{Binding EventXml, XPath=M_EVENT[ROW_STATUS=1]}"
...>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding XPath=EVENT_NAME}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
This is better than the DataTable technique, but to add your "Select Event" row will require a CompositeCollection to be added. That can be done, but...
The best solution is to use LINQ to XML:
public object Events
{
get
{
return new[] { new { Name = "--Select Event--", Id = -1 }}.Concat(
from element in EventsXml.Elements("M_EVENT")
where element.Element("ROW_STATUS").Value=="1"
select new
{
Name = element.Element("EVENT_NAME").Value,
Id = int.Parse(element.Element("PK_EVENT_ID").Value),
});
}
}
With this simple XAML:
<ComboBox ItemsSource="{Binding Events}" SelectedValuePath="Id" ...>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
The LINQ to XML solution is faster than binding to XML, which in turn is faster than using DataTable. Not only that, but the LINQ to XML solution is much cleaner than the other two.
Related
I have a dropdown column in my datagrid, the options for the combobox are stored in the tmfCNCComponentStatus_threed table. My main table has a column called [Status] which corresponds to the key column in that table.
The correct status [Description] is displayed in the combobox for each row in my datagrid, but when the user changes the selection, the database isn't updating, even though everything looks as though it is working properly. I have the "UpdateSourceTrigger" set to PropertyChanged as seen in many similar posts but still no dice. Any insight would be greatly appreciated!
<CollectionViewSource x:Key="StatusItems"/>
<DataGridTemplateColumn x:Name="StatusColumn" Header="Status" Width="*" IsReadOnly="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="cboStatus" Text="{Binding Path=Description}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox x:Name="StatusCombo" SelectedValuePath="CNCComponentStatusKey" DisplayMemberPath="Description" SelectedValue="{Binding Status, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding Source={StaticResource StatusItems}}" IsEditable="True" IsSynchronizedWithCurrentItem="True"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
The code behind:
Dim com As String = "SELECT tmfCNCComponent_threed.[CNCComponentKey]
,tmfCNCComponent_threed.[CompanyID]
,tmfCNCComponent_threed.[JobNumber]
,tmfCNCComponent_threed.[LogNumber]
,tmfCNCComponent_threed.[Customer]
,tmfCNCComponent_threed.[DueDate]
,tmfCNCComponent_threed.[JobLeader]
,tmfCNCComponent_threed.[CADProgrammer]
,tmfCNCComponent_threed.[Salesperson]
,tmfCNCComponent_threed.[CNCProgrammer]
,tmfCNCComponent_threed.[ComponentDescription]
,tmfCNCComponent_threed.[ComponentFilePath]
,tmfCNCComponent_threed.[Material]
,tmfCNCComponent_threed.[ComponentSizeX]
,tmfCNCComponent_threed.[ComponentSizeY]
,tmfCNCComponent_threed.[ComponentSizeZ]
,tmfCNCComponent_threed.[QuantityShown]
,tmfCNCComponent_threed.[QuantityMirror]
,tmfCNCComponent_threed.[UpdateTime]
,tmfCNCComponent_threed.[Status]
,tmfCNCComponent_threed.[ProgStarted]
,tmfCNCComponentStatus_threed.[Description]
FROM [test_3DimensionalDB].[dbo].[tmfCNCComponent_threed]
INNER JOIN tmfCNCComponentStatus_threed
ON tmfCNCComponent_threed.Status = tmfCNCComponentStatus_threed.CNCComponentStatusKey
WHERE [ComponentDescription] " & component & " 'trode%' AND [CompanyID]='" & company & "' AND [Status]" & status & "ORDER BY [UpdateTime] DESC"
Dim Adpt As New SqlDataAdapter(com, con)
con.Open()
Dim ds As New DataSet()
Adpt.Fill(ds, "dbo.tmfCNCComponent_threed")
dataGrid1.ItemsSource = ds.Tables("dbo.tmfCNCComponent_threed").DefaultView
con.Close()
con.Open()
Dim statusCVS As CollectionViewSource = FindResource("StatusItems")
Dim com2 As String = "SELECT * FROM tmfCNCComponentStatus_threed"
Dim AdptStatus As New SqlDataAdapter(com2, con)
AdptStatus.Fill(ds, "dbo.tmfCNCComponentStatus_threed")
Dim statusRows = ds.Tables("dbo.tmfCNCComponentStatus_threed").Rows
Dim statuses As New List(Of Object)
For Each row As DataRow In statusRows
statuses.Add(New With {
.Status = CInt(row("CNCComponentStatusKey")),
.Description = CStr(row("Description"))
})
Next
statusCVS.Source = statuses
con.Close()
RowCount()
searchBox.Clear()
Thanks for your time.
The Database:
Contents of the Status table:
The Datagrid:
Here is the first part of this question that was addressed yesterday to get me to this point:
Part 1
Based on information in comments in a different question, you probably need to change SelectedValuePath="Status" to SelectedValuePath="CNCComponentStatusKey". The names of the columns or properties of the items in the combobox are critical to answering this question, and you haven't provided them.
The grid will update the DataRowView column value when the cell leaves edit mode.
Mode=TwoWay on that binding is unnecessary. That's the default mode for bindings on ComboBox.SelectedValue.
You can remove all those decorations from the binding on TextBlock.Text: It can't update the source at all, when you think about it, so there's no need to clutter up your XAML with elaborate instructions about how and when it should do something it can't do anyway.
Complete working example
This is the code with which I tested the above answer. It updates the rows in the main table. It doesn't save the table to a database. That's a separate thing.
I don't know if your columns aren't called what you think they are, or what.
MainWindow.xaml.cs
public MainWindow()
{
InitializeComponent();
LoadData();
}
#region Lists
private static List<String> _status = new List<String>
{
"Ready",
"Not Ready",
"Weary",
"Disordered",
};
private static List<String> _words = new List<String>
{
"Ewwrigowasaus",
"Skubreuph",
"Creecroicr",
"Eunthaudrawtr",
"Ootwoww",
"Meuleetroithr",
"Rewshr",
"Scoysl",
"Scewziexul",
"Kawxayzeec",
};
#endregion Lists
protected void LoadData()
{
DataTable dtMain = new DataTable();
dtMain.Columns.Add("Status", typeof(int));
dtMain.Columns.Add("Programmer", typeof(String));
_words.ForEach(w =>
{
var row = dtMain.NewRow();
row[0] = ((int)w[0] % 2) + 1;
row[1] = w;
dtMain.Rows.Add(row);
});
DataTable dtStatus = new DataTable();
dtStatus.Columns.Add("CNCComponentStatusKey", typeof(int));
dtStatus.Columns.Add("Description", typeof(String));
_status.ForEach(s =>
{
var row = dtStatus.NewRow();
row[0] = dtStatus.Rows.Count + 1;
row[1] = s;
dtStatus.Rows.Add(row);
});
DataGrid.ItemsSource = dtMain.DefaultView;
var cvs = (FindResource("StatusItems") as CollectionViewSource);
cvs.Source = dtStatus.DefaultView;
}
MainWindow.xaml
<Window.Resources>
<CollectionViewSource x:Key="StatusItems" />
</Window.Resources>
<Grid>
<DataGrid x:Name="DataGrid" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Programmer}" Header="Programmer" />
<DataGridTemplateColumn Header="Status">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Status}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ComboBox
ItemsSource="{Binding Source={StaticResource StatusItems}}"
SelectedValue="{Binding Status, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Description"
SelectedValuePath="CNCComponentStatusKey"
x:Name="Combo"
/>
<!-- Selected value in combo -->
<Label Content="{Binding SelectedValue, ElementName=Combo}" />
<!-- Value of Status column in row -->
<Label Content="{Binding Status}" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
I have two wpf tool kit charts one is pie chart and second bar series
i have method which i call only on form load
chartGuest.DataContext = null;
List<KeyValuePair<string, int>> valueList = new List<KeyValuePair<string, int>>();
HProDataContext db = new HProDataContext();
var _RoomTypes = (from d in db.roomtypes select d.roomtype1).ToList();
var _RoomTypeID = (from d in db.roomtypes select d.id).ToList();
int count = 0;
for (int i = 0; i < _RoomTypeID.Count; i++)
{
count = Convert.ToInt32((from d in db.actions where d.room.roomtypeid == _RoomTypeID[i] select d.id).Count());
valueList.Add(new KeyValuePair<string, int>(_RoomTypes[i], count));
}
chartGuest.DataContext = valueList;
It gaves such error : Cannot modify the logical children for this node at this time because a tree walk is in progress.
The same code works great on pie series chart.
This is my charts:
<charting:Chart x:Name="chartRoomType" Width="402" Height="255" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="15,275,0,0">
<charting:Chart.Series>
<charting:PieSeries ItemsSource="{Binding}" DependentValuePath="Value" IndependentValuePath="Key" Title="Room Types" IsSelectionEnabled="True" />
</charting:Chart.Series>
</charting:Chart>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="314,292,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
<TextBlock Height="23" HorizontalAlignment="Left" Margin="28,296,0,0" Name="textBlock4" Text="Room Types" VerticalAlignment="Top" />
<charting:Chart x:Name="chartGuest" Height="269" VerticalAlignment="Top" Margin="6,0" Title="Guests">
<charting:Chart.Series>
<charting:BarSeries ItemsSource="{Binding}" DependentValuePath="Value" IndependentValuePath="Key" Title="Room Types" IsSelectionEnabled="True" />
</charting:Chart.Series>
</charting:Chart>
Can anyone help me?
P.s. i found this question but it was unhelpful
What does Cannot modify the logical children for this node at this time because a tree walk is in progress mean?
Use DataPointSeries.ItemsSource to bind to the data context.
Assuming that this is XAML of your your window panel that has the datagrid and charting control sharing a common list as ItemsSource..
<StackPanel>
<tk:DataGrid MaxHeight="200" AutoGenerateColumns="False"
ItemsSource="{Binding}"
IsReadOnly="True">
<tk:DataGrid.Columns>
<tk:DataGridTextColumn Header="Key"
Binding="{Binding Key, Mode=OneWay}"/>
<tk:DataGridTextColumn Header="Value"
Binding="{Binding Value, Mode=OneWay}"/>
</tk:DataGrid.Columns>
</tk:DataGrid>
<charting:Chart MaxHeight="300"
Title="Title"
LegendTitle="Legend"
Name="Chart1">
<charting:AreaSeries DependentValuePath="Value"
IndependentValuePath="Key"
Background="Red" >
<charting:DataPointSeries.ItemsSource>
<Binding BindsDirectlyToSource="True"/>
</charting:DataPointSeries.ItemsSource>
</charting:AreaSeries>
</charting:Chart>
<Button Content="Change DataGrid and Chart Data" Click="Button_Click"/>
</StackPanel>
In code behind we reset the data context of the window ....
private List<KeyValuePair<int, int>> list1;
public Window1()
{
InitializeComponent();
list1 = new List<KeyValuePair<int, int>>();
var random = new Random();
for(int i = 0; i < 1000; i++)
{
list1.Add(new KeyValuePair<int, int>(i, random.Next()));
}
this.DataContext = list1;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
list1 = new List<KeyValuePair<int, int>>();
var random = new Random();
for (int i = 0; i < 1000; i++)
{
list1.Add(new KeyValuePair<int, int>(i, random.Next()));
}
this.DataContext = list1;
}
Everytime you click the button the chart refreshes without error.
Let me know if this helps.
I've received the same exception in different situation. I work with code which takes collection of existing UIElements and create new UIElement and set first one as Content of second one. Aditionally, it takes property value from existing UIElement and set it as value of different property of new UIElement.
foreach(UIElement insideElement in uiElementCollection)
{
var outsideElement = new TabItem
{
Content = insideElement
,Header = insideElement.Title
}
}
But in this case Title of inside property can be a Binding, but code above copies only value and when insideElement.Title is bounded to some source, outsideElement.Header doesn't reflect change.
So I changed code to bind HeaderProperty to TitleProperty of insideElement:
foreach(UIElement insideElement in uiElementCollection)
{
var outsideElement = new TabItem
{
Content = insideElement
}
outsideElement.SetBinding(HeaderedContentControl.HeaderProperty,
new Binding
{
Source = insideElement,
Path = new PropertyPath(MyUIElement.TitleProperty)
});
}
But when I executed code above, I received
System.InvalidOperationException: Cannot modify the logical children for this node at this time because a tree walk is in progress.
I found that I can't bind one dependency property to another one, which is stored inside Content property.
I tried to get binding expression of innerUIElement and set it as new binding to HeaderProperty of outsideUIElement.
foreach(UIElement insideElement in uiElementCollection)
{
var outsideElement = new TabItem
{
Content = insideElement
}
BindingExpression titleBindingExpression = pageControl.GetBindingExpression(MyUIElement.TitleProperty);
if (titleBindingExpression != null)
{
tabItem.SetBinding(HeaderedContentControl.HeaderProperty, new Binding { Source = titleBindingExpression.DataItem, Path = titleBindingExpression.ParentBinding.Path });
}
else
{
tabItem.Header = innerUIElement.Title;
}
}
Now, outer UI Element is binded to same source and WPF presenter doesn't throw Exception. My problem is solved.
I know this has been a while, but in case anyone else comes across it I found another way around it. Put the chart into a StackPanel, created with Visibility of "Collapsed" ("Hidden" DOES NOT WORK). Then set visibility to "Visible" after setting DataContext the first time (I'm doing it every time, but after the first it's redundant).
My experience agrees with reports I've seen that the problem is a bug that occurs when setting a non-empty DataContext after having had an empty one; but apparently it doesn't do the "tree walk" if the chart is collapsed.
Here is my code to create cascading comboboxes. I am trying to populate Family Combobox(ComboBox2) based on the value selected for Segment Name(combox1).I am able to populate the first Combo with a static resource but my second one shows blank.
I am calling a method called "FillComboBoxFamilyData(SegmentCode)" on the selection change event of first combobox.
XAML CODE:
<Grid Height="142" HorizontalAlignment="Left" Margin="49,113,0,0" Name="grid3" VerticalAlignment="Top" Width="904">
<ComboBox Height="23" HorizontalAlignment="Left" Margin="35,26,0,0" Name="comboBox1" VerticalAlignment="Top" Width="205" ItemsSource="{Binding Source={StaticResource tblSegmentViewSource}}" DisplayMemberPath="Segment Name" SelectedValuePath="Segment Code" SelectionChanged="comboBox1_SelectionChanged"/>
<ComboBox Height="23" HorizontalAlignment="Right" Margin="0,26,395,0" Name="comboBox2" VerticalAlignment="Top" Width="205" />
<Window.Resources>
<CollectionViewSource x:Key="tblSegmentViewSource" Source="{Binding Path=TblSegment, Source={StaticResource brickDataset}}" />
<CollectionViewSource x:Key="tblFamilyViewSource" Source="{Binding Path=TblFamily, Source={StaticResource brickDataset}}" />
*BrickDataSet is the main dataset I am pulling the tables from.*
C#:
private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
MessageBox.Show(comboBox1.SelectedValue.ToString());
SegmentCode = Convert.ToInt32(comboBox1.SelectedValue.ToString());
FillComboBoxFamilyData(SegmentCode);
}
public void FillComboBoxFamilyData(int Segment_Code)
{
string connString =
"Data Source=.\\SQLEXPRESS;AttachDbFilename=C:\\Documents and Settings\\dchaman\\My Documents\\PDRT.mdf;Integrated Security=True;Connect Timeout=30;User Instance=True ";
SqlConnection con = new SqlConnection(connString);
con.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText =
"SELECT TblFamily.[Family Name],TblFamily.[Family Code] FROM TblFamily WHERE (TblFamily.[Segment Code] = #SegmentCode)";
cmd.Parameters.AddWithValue("#SegmentCode", Segment_Code);
DataSet objDs = new DataSet();
SqlDataAdapter dAdapter = new SqlDataAdapter();
dAdapter.SelectCommand = cmd;
dAdapter.Fill(objDs);
con.Close();
if (objDs.Tables[0].Rows.Count > 0)
{
MessageBox.Show("Here I am");
comboBox2.DataContext = objDs.Tables;
comboBox2.Items.Insert(0, "--Select Family Name--");
comboBox2.DisplayMemberPath = "Family Name";
comboBox2.SelectedValue = "Family Code";
}
}
I am banging my head with the table right now.Please Save me!
You aren't setting the comboBox2's ItemsSource. The DataContext will simply effect all binding statements on it. If you set the ItemsSource to the correct table instead of the DataContext, it should get you on the right path:
if (objDs.Tables[0].Rows.Count > 0)
{
MessageBox.Show("Here I am");
comboBox2.ItemsSource = ((IListSource)objDs.Tables[0]).GetList(); // set the ItemsSource instead of the DataContext
comboBox2.DisplayMemberPath = "Family Name";
comboBox2.SelectedValue = "Family Code";
}
Note that when you set the ItemsSource, the line that inserts text into the Items property will fail, as you can not use both the ItemsSource and the Items together.
I am working on a datagrid in silverlight. I have a WCF service that returns a List that works fine when I populate a datagrid. CoreEmployee returns properties of EmployeeId, FirstName, LastName, HourlyRate, HireDate. This is my XAML of the hourly rate:
<data:DataGridTemplateColumn Header="Hourly Rate">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding HourlyRate}" />
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
<data:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<!--ItemsSource="{Binding PayRateList, Source={StaticResource PayRateProvider}}"-->
<ComboBox SelectedItem="{Binding HourlyRate}"
ItemsSource="{Binding HourlyRates}"
local:ComboBoxService.ForceOpen="true"
/>
</DataTemplate>
</data:DataGridTemplateColumn.CellEditingTemplate>
</data:DataGridTemplateColumn>
Here is what I am trying to accomplish: When the hourly rates for each employee is populated on the data grid, I also want a list of all the unique salary rates for each person in the data grid.
My code behind does do that:
private List<Decimal> _hourlyRates = new List<decimal>();
public List<Decimal> HourlyRates
{
get { return _hourlyRates; }
}
void client_GetEmployeesCompleted(object sender, GetEmployeesCompletedEventArgs e)
{
try
{
if (e.Result != null)
{
EmployeesGrid.ItemsSource = e.Result;
// Convert an ObservableCollection<T> to a List<T>
List<CoreEmployee> employees = e.Result.ToList<CoreEmployee>();
// Generate a unique list
// http://stackoverflow.com/questions/223400/checking-for-duplicates-in-a-complex-object-using-linq-or-lamda-expression
var results = from item in employees
group item by item.HourlyRate into g
select g.First();
foreach (CoreEmployee employee in results)
{
HourlyRates.Add(employee.HourlyRate);
}
_dataHasLoaded = true;
}
}
catch (Exception exc)
{
// Eat the exception
}
}
However, the problem occurs when I attempt to double click the textblock, the combo box does display, but without any data.
What am I doing wrong?
You should raise the PropertyChanged event on the HourlyRates property after populating the list in the foreach loop. Also, set the Mode of the ComboBox SelectedItem binding to TwoWay.
I want to bind the columns of my WPF DataGrid to some objects in a Dictionary like this:
Binding Path=Objects[i]
where Objects is my Dictionary of objects, so that each cell will represent an Object element. How can I do that?
I suppose that I need to create a template for my cell, which I did, but how to get the result of column binding in my template? I know that by default the content of a DataGridCell is a TextBlock and it's Text property is set through column binding result, but if that result is an object I guess that I have to create a ContentTemplate. How do I do that, as the stuff I tried is not displaying anything.
Here it is what I tried:
<Style x:Key="CellStyle" TargetType="{x:Type dg:DataGridCell}">
<Setter Property="Template"> ---it should realy be ContentTemplate?
<Setter.Value>
<ControlTemplate>
<controls:DataGridCellControl CurrentObject="{Binding }"/> -- I would expect to get the object like this for this column path : Path=Objects[i] but is not working
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
So, to make myself completly clear, i want to get in CurrentObject property of my DataGridCellControl the current object that should result if I set the column binding in my data grid like this Path=Objects[i].
Thank you for any suggestion,
John.
Try this:
<ListView x:Name="listViewUsers" SelectionMode="Single"
ItemsSource="{Binding ElementName=window1, Path=Users, Mode=TwoWay}" MouseDoubleClick="listViewUsers_MouseDoubleClick">
<ListView.View>
<GridView x:Name="gridViewUsers" AllowsColumnReorder="False">
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding Path=IsAdministrator, Converter={StaticResource boolToImage}, ConverterParameter='Images/admin18.gif|Images/user18.gif'}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="User Name" DisplayMemberBinding="{Binding Path=UserName}" Width="140" />
<GridViewColumn Header="Full Name" DisplayMemberBinding="{Binding Path=FullName}" Width="140" />
<GridViewColumn Header="Phone Number" DisplayMemberBinding="{Binding Path=PhoneNumber}" Width="110" />
<GridViewColumn Header="Access Type" DisplayMemberBinding="{Binding Path=AccessType}" Width="110">
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Cursor="Hand" ToolTip="Delete User" Stretch="None" Source="Images/trash12.gif" MouseUp="DeleteUser" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Where in ItemsSource="{Binding ElementName=window1, Path=Users, Mode=TwoWay}"
ElementName is the name of the Window in XAML (just add x:Name="window1" to the Window tag as with any other ontrol.
Users is a List, should work the same with Dictionary
Mode=TwoWay means that if the grid gets modified, the list will get modified too, and vice versa (Two way binding)
EDIT:
Try this:
XAML:
<ListView x:Name="listViewTest" ItemsSource="{Binding}">
<ListView.View>
<GridView x:Name="gridViewTest">
</GridView>
</ListView.View>
</ListView>
C#:
public class TheClass
{
public int Col1, Col2, Col3;
public Dictionary<int, OtherColumns> otherColumns = new Dictionary<int,OtherColumns>();
}
public class OtherColumns
{
public string ColumnName;
public int Value;
}
And call this method under Window_Loaded:
private void PopulateListView()
{
TheClass c = new TheClass();
c.Col1 = 10;
c.Col2 = 20;
c.Col3 = 30;
c.otherColumns.Add(0, new OtherColumns() { ColumnName = "Col4", Value = 40 });
c.otherColumns.Add(1, new OtherColumns() { ColumnName = "Col5", Value = 50 });
c.otherColumns.Add(3, new OtherColumns() { ColumnName = "Col6", Value = 60 });
DataTable table = new DataTable();
// adding regular columns
table.Columns.Add("Col1", typeof(int));
table.Columns.Add("Col2", typeof(int));
table.Columns.Add("Col3", typeof(int));
// adding dynamic columns
foreach (KeyValuePair<int, OtherColumns> pair in c.otherColumns)
{
table.Columns.Add(pair.Value.ColumnName, typeof(int));
}
DataRow row = table.NewRow();
// adding regular column values to the DataTable
row["Col1"] = c.Col1;
row["Col2"] = c.Col2;
row["Col3"] = c.Col3;
// adding dynamic column values to the DataTable
foreach (KeyValuePair<int, OtherColumns> pair in c.otherColumns)
{
row[pair.Value.ColumnName] = pair.Value.Value;
}
table.Rows.Add(row);
// Start binding the table.
gridViewTest.Columns.Clear();
System.Windows.Controls.GridViewColumn gvc;
Binding binding;
foreach (DataColumn column in table.Columns)
{
gvc = new System.Windows.Controls.GridViewColumn();
binding = new System.Windows.Data.Binding();
binding.Path = new PropertyPath(column.ColumnName);
binding.Mode = BindingMode.OneWay;
gvc.Header = column.Caption;
gvc.DisplayMemberBinding = binding;
gridViewTest.Columns.Add(gvc);
}
listViewTest.DataContext = table;
}
I'm not saying it's the best solution, but it could help. Let me know.
I made some helper classes so I could use the DataGrid as a kind of DataTable. In other words, I wanted the formatting, sorting, and polished look of the DataGrid without having to pre-fab some classes beforehand. The main reason I wanted this was for a testing suite, I wanted to be able to create an arbitrary number of columns and at runtime. Here's what I got
public class DataRow
{
internal List<object> Items = new List<object>();
public object this[string value]
{
get { return Items[Convert.ToInt32(value)]; }
}
public string GetString(int index)
{
return Items[index].ToString();
}
public object GetObject(int index)
{
return Items[index];
}
public DataRow(params object[] values)
{
if (values == null || values.Length < 1)
throw new Exception("You must pass in some values");
Items.AddRange(values);
}
}
public class GridConstructor
{
public List<DataRow> Rows = new List<DataRow>();
private DataRow headers;
public GridConstructor(DataRow head)
{
headers = head;
}
public void BuildInto(DataGrid grid)
{
grid.AutoGenerateColumns = false;
grid.Columns.Clear();
int totalCols = 0;
Type headType = headers.GetType();
for (int i = 0; i < headers.Items.Count; i++)
{
grid.Columns.Add(GetCol(headers.GetString(i), String.Concat("[", i.ToString(),"]")));
totalCols++;
}
int finalWidth = totalCols * (int)grid.ColumnWidth.Value + 15;
grid.Width = finalWidth;
grid.ItemsSource = Rows;
}
private DataGridTextColumn GetCol(string header, string binding)
{
DataGridTextColumn col = new DataGridTextColumn();
col.IsReadOnly = true;
col.Header = header;
col.Binding = new Binding(binding);
return col;
}
public DataGrid Create(int colSize)
{
DataGrid grid = new DataGrid();
grid.ColumnWidth = colSize;
grid.CanUserAddRows = false;
grid.AlternationCount = 2;
BuildInto(grid);
return grid;
}
}
Putting this together, this is a sample use:
void SimpleTest_Loaded(object sender, RoutedEventArgs e)
{
DataRow headers = new DataRow("Level", "Weapon Type", "vs None", "vs Leather", "vs Studded", "vs Brigandine");
GridConstructor gridConstructor = new GridConstructor(headers);
var weaponType = "Slash";
for (int level = 1; level < 10; level++)
{
int damage = DiceCup.RollMulti(8, level);
int damCloth = damage - DiceCup.RollMulti(2, level);
int damLeather = damage - DiceCup.RollMulti(3, level);
int damStudded = damage - DiceCup.RollMulti(4, level);
int damBrigandine = damage - DiceCup.RollMulti(5, level);
DataRow row = new DataRow(level, weaponType, damage, damCloth, damLeather, damStudded, damBrigandine);
gridConstructor.Rows.Add(row);
}
//Create the grid.
var grid = gridConstructor.Create(100);
//Create a chart.
Chart chart = new Chart();
chart.Height = 200;
chart.LegendTitle = "Legend";
chart.Title = "Slash vs Armor Types";
chart.DataContext = gridConstructor.Rows;
//Create our series, or lines.
LineSeries slashVsNone = new LineSeries();
slashVsNone.Title = "vs None";
slashVsNone.DependentValueBinding = new Binding("[2]");
slashVsNone.IndependentValueBinding = new Binding("[0]");
slashVsNone.ItemsSource = gridConstructor.Rows;
chart.Series.Add(slashVsNone);
//Presentation is a stackpanel on the page.
presentation.Children.Add(grid);
presentation.Children.Add(chart);
}
And the output:
alt text http://quiteabnormal.com/images/codeSample.jpg
Please note that the grid coloring is from universal styles set on the page. If you use the GridConstructor.BuildInto() method you can specify a grid you've pre-formatted yourself in Blend or somesuch.
Just one thing, the GridConstructor makes some assumptions about the column's initial settings. You can change the class to make it more customizable if you like, but this is what I needed so I wanted to be able to make it without fuss.