Excessive XAML markup size in Silverlight RichTextBox - silverlight

I have a very simple RichTextBox control in a silverlight 5 application.
<RichTextBox x:Name="rtbControl"
Height="Auto"
ContentChanged="rtbControl_ContentChanged" />
As the data is changed, i capture it in a local variable as so:
private void rtbControl_ContentChanged(object sender, ContentChangedEventArgs e)
{
RichTextContent = rtbControl.Xaml;
}
All works. However, if I type in just a few words and examine the <Paragraph> markup, it is HUGE! A snippet is below. Is there a way to not have all the Typography stuff in the markup? Why is it there and is it needed? Is there a way to remove it?
<Section xml:space="preserve" HasTrailingParagraphBreakOnPaste="False" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Paragraph FontSize="11" FontFamily="Segoe UI" Foreground="#FF000000" FontWeight="Normal" FontStyle="Normal" FontStretch="Normal"
CharacterSpacing="0" Typography.AnnotationAlternates="0" Typography.EastAsianExpertForms="False" Typography.EastAsianLanguage="Normal"
Typography.EastAsianWidths="Normal" Typography.StandardLigatures="True" Typography.ContextualLigatures="True"
Typography.DiscretionaryLigatures="False" Typography.HistoricalLigatures="False" Typography.StandardSwashes="0" Typography.ContextualSwashes="0"
Typography.ContextualAlternates="True" Typography.StylisticAlternates="0" Typography.StylisticSet1="False" Typography.StylisticSet2="False"
Typography.StylisticSet3="False" Typography.StylisticSet4="False" Typography.StylisticSet5="False" Typography.StylisticSet6="False" Typography.StylisticSet7="False"
Typography.StylisticSet8="False" Typography.StylisticSet9="False" Typography.StylisticSet10="False" Typography.StylisticSet11="False"
Typography.StylisticSet12="False" Typography.StylisticSet13="False" Typography.StylisticSet14="False" Typography.StylisticSet15="False"
Typography.StylisticSet16="False" Typography.StylisticSet17="False" Typography.StylisticSet18="False" Typography.StylisticSet19="False"
Typography.StylisticSet20="False" Typography.Capitals="Normal" Typography.CapitalSpacing="False" Typography.Kerning="True" Typography.CaseSensitiveForms="False"
Typography.HistoricalForms="False" Typography.Fraction="Normal" Typography.NumeralStyle="Normal" Typography.NumeralAlignment="Normal" Typography.SlashedZero="False"
Typography.MathematicalGreek="False" Typography.Variants="Normal" TextOptions.TextHintingMode="Fixed" TextOptions.TextFormattingMode="Ideal"
TextOptions.TextRenderingMode="Auto" TextAlignment="Left" LineHeight="0" LineStackingStrategy="MaxHeight"><Run>was it a </Run><Run TextDecorations="Underline">bad
</Run><Run>blah? or </Run></Paragraph></section>

I thought I would share my solution to help others... its been in place for several months and I havent seen any bad side effects.
I created some extention methods to assist in removing the Typography attributes.
public static class RichTextBoxExtensions {
// <summary>
/// Removes any xml tag with the prefix given from a string
/// </summary>
/// <param name="XMLString"></param>
/// <param name="TagPrefix"></param>
/// <returns></returns>
public static string RemoveXMLAttributesFromNode(this string XMLString, string prefix)
{
//Match [Prefix][any number of Not =][=]["][any number of Not "]["][ ] <-must have space!!!
string replace = prefix + "[^\\=]*=\"[^\"]*\" ";
return Regex.Replace(XMLString, replace, string.Empty);
}
/// <summary>
/// Removes any xml tag with prefixed by an element in unWanted
/// </summary>
/// <param name="XMLString"></param>
/// <param name="TagPrefix"></param>
/// <returns></returns>
public static string RemoveXMLAttributesFromNode(this string XMLString, List<string> unWanted)
{
foreach (string prefix in unWanted)
{
//Match [Prefix][any number of Not =][=]["][any number of Not "]["][ ] <-must have space!!!
string replace = prefix + "[^\\=]*=\"[^\"]*\" ";
XMLString = Regex.Replace(XMLString, replace, string.Empty);
}
return XMLString;
}
Next, right before I save my string to the database, I remove the bad attributes as so:
List<string> badAttributes = new List<string>();
badAttributes.Add("Typography");
var ffText = ffi.FreeFormText == null ? null : ffi.FreeFormText.RemoveXMLAttributesFromNode(badAttributes);
I have not seen any issues when re-rendering the RTF after removing these attributes. Hope this solution helps someone!

Related

How auto update db from datagrid

I am using VB/wpf and SQL Server. I am NOT using mvvm, a datatable, a button, a dataset or a windows form. Under those conditions and a couple of days of searching, I cannot find the final step to saving the data from a grid.
My database table tracks donations; it has the usual, expected fields. Here is what I have so far:
XAML (snippet):
<Window.Resources>
<CollectionViewSource
Filter="Filter_By_Member"
x:Key="cvsDonations">
</CollectionViewSource>
.....
<DataGrid
AutoGenerateColumns="False"
CanUserReorderColumns="True"
CanUserResizeColumns="True"
CanUserResizeRows="False"
CanUserDeleteRows="True"
FontSize="13"
FontWeight="Normal"
ItemsSource="{Binding Source={StaticResource cvsDonations}}"
Name="dgDonations"
RowHeaderWidth="20"
SelectionUnit="CellOrRowHeader"
SelectionMode="Single">
<DataGrid.Columns>
<DataGridTextColumn
Binding="{Binding Path=DateDue, Converter={StaticResource conDate}, StringFormat='MMM d, yyyy'}"
ElementStyle="{StaticResource styDateBlock}"
Header="Date Due"
IsReadOnly="True"
Width="90">
</DataGridTextColumn>
.....
My window contains a ComboBox and the grid. The rows in the grid depend on what is selected in the ComboBox. Here is the code that does that.
Private Sub Change_Member(sender As Object, e As SelectionChangedEventArgs) Handles cboMembers.SelectionChanged
cvsDonations.View.Refresh()
End Sub
Private Sub Filter_By_Member(sender As Object, e As FilterEventArgs)
Dim PersonID As Long = CLng(cboMembers.SelectedValue)
Dim d As Donation = DirectCast(e.Item, Donation)
If d.PersonID = PersonID Then
e.Accepted = True
Else
e.Accepted = False
End If
End Sub
So far everything works. What I haven't figured out is how to automatically update the underlying database (using stored procedures which are already written and working). More specifically, I cannot figure out what event to watch for and how to implement that event in code.
The underlying collection is an ObservableCollection(Of Donation) and the Donation class has implemented INotifyPropertyChanged. I've considered watching the property changed but that event gets fired every time the grid is populated so that doesn't seem like the right approach, besides I can't figure out how to actually raise the appropriate event even if that is the way to go.
I've tried using the RowEditChanging event which seems to be ALMOST what I want but it is fired before any edits are submitted so the donation the row contains has the old value when this event is fired.
What is the right approach and how should it be implemented given my overall set up?
I suggest doing it this way -
Your Donation class should have a flag field, let's say Is_Dirty which keeps track of whether the object has modifications or not.
When you initially load items in your collection, make sure Is_Dirty is set to false. When user makes edit on grid and property notification is fired, set the dirty flag (from property setter).
For saving, I suggest having a separate button, clicking on which you go through all dirty objects from your collection and pass updates to database.
By binding to dirty flag, you can also change the appearance of modified rows for user to identify modified rows.
You can also maintain a State property, to know whether the object is Added, Updated or Deleted.
Typically, I put all this functionality in a base class from which all my model classes (e.g. Donation) derives.
Some snippet to get you going...
public abstract class ObservableDomainObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _isDirty;
public ObservableDomainObject()
{
_state = ObjectState.Added;
_isDirty = true;
}
private ObjectState _state;
/// <summary>
/// Get or Set
/// </summary>
public ObjectState State
{
get { return _state; }
set
{
if (_state != value)
{
_state = value;
RaisePropertyChanged();
}
}
}
public void SetDirty(Boolean isDirty)
{
this.IsDirty = isDirty;
}
/// <summary>
/// Check if the object is modified.
/// </summary>
public bool IsDirty
{
get { return _isDirty; }
set
{
if (_isDirty != value)
{
if (_state != ObjectState.Added && _state != ObjectState.Deleted)
{
this.State = value ? ObjectState.Modified : ObjectState.Unchanged;
}
_isDirty = value;
RaisePropertyChanged();
}
}
}
protected virtual void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
// Change Dirty flag when property changed.
if (propertyName != "IsDirty")
SetDirty(true);
}
}
public enum ObjectState
{
/// <summary>
/// New Record
/// </summary>
Added = 0,
/// <summary>
/// Unchanged after the load
/// </summary>
Unchanged,
/// <summary>
/// Modified
/// </summary>
Modified,
/// <summary>
/// Deleted
/// </summary>
Deleted
}

Can i serialize and deserialize a DataRowView from a WPF ComboBox?

I'm filling the itemssource of a WPF combobox in code-behind with a datatable containing the columns "Listkey" and "Listvalue" like that:
SetElementProperty(element, "ItemsSource", (new ListtablesRead()).ReadListtable(changeTextProperties.SelectedListTable).DefaultView);
SetElementProperty(element, "DisplayMemberPath", "Listvalue");
SetElementProperty(element, "SelectedValuePath", "Listkey");
SetElementProperty is a method which checks by reflection, if the frameworkelement (in that case the combobox) has the given property and sets it.
Then i want to serialize the control with XmlWriter.
So i wrote a converter class for the type DataRowView:
using System;
using System.ComponentModel;
using System.Data;
using System.Windows;
using System.Windows.Markup;
namespace WPFDesignerConverterLibrary
{
public class DataRowViewConverter : ExpressionConverter
{
/// <summary>
/// Finds out if the converter can convert an expression-object to the given destinationtype.
/// </summary>
/// <param name="context">An ITypeDescriptorContext-interface which provides a context for formatting.</param>
/// <param name="destinationType">A type-class which represents the target-type of the conversion.</param>
/// <returns>Returns an object of type bool. True = the destinationtype can be converted.</returns>
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(MarkupExtension))
return true;
return false;
}
/// <summary>
/// Converts the expression to the given destinationtype.
/// </summary>
/// <param name="context">An ITypeDescriptorContext-interface which provides a context for formatting.</param>
/// <param name="culture">The System.Globalization.CultureInfo which is actually used as culture.</param>
/// <param name="value">The object to convert.</param>
/// <param name="destinationType">A type-class which represents the target-type of the conversion.</param>
/// <returns></returns>
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value,
Type destinationType)
{
if (destinationType == typeof(MarkupExtension))
{
DataRowView datarowview = value as DataRowView;
if (datarowview == null)
throw new Exception();
return datarowview.Row;
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
}
This converter works and produces the following lines in the serialized XML:
<sd:DataRow RowError="">
<sd:DataRow.ItemArray>
<x:Array Type="s:Object" xml:space="preserve"><s:String>01</s:String><s:String>Ersttest </s:String></x:Array>
</sd:DataRow.ItemArray>
</sd:DataRow>
<sd:DataRow RowError="">
<sd:DataRow.ItemArray>
<x:Array Type="s:Object" xml:space="preserve"><s:String>02</s:String><s:String>Wiederholungstest </s:String></x:Array>
</sd:DataRow.ItemArray>
</sd:DataRow>
<sd:DataRow RowError="">
<sd:DataRow.ItemArray>
<x:Array Type="s:Object" xml:space="preserve"><s:String>03</s:String><s:String>Konstanzprüfung </s:String></x:Array>
</sd:DataRow.ItemArray>
</sd:DataRow>
But when i try to reload the serialized XML i get an error message, which says, that no standard constructor for the type DataRow was found.
What's going wrong?
And further: To set the itemssource of the combobox with a datatable's defaultview is the simplest way to do this, but can it be that i have to go another way?
InnerException:
=-2146233069 HResult
Message=For the type "System. Data. DataRow" no standard constructor was found. The type can be provided with the argument or the FactoryMethod directive.
Source=System. Xaml
StackTrace:
at system. Xaml. Pattern. XamlTypeInvoker. DefaultCtorXamlActivator. EnsureConstructorDelegate (XamlTypeInvoker type)
at system. Xaml. Pattern. XamlTypeInvoker. CreateInstance (Object [] of argument)
at MS.Internal. Xaml. Run time. ClrObjectRuntime. CreateInstanceWithCtor (XamlType xamlType, Object [] args)
at MS.Internal. Xaml. Run time. ClrObjectRuntime. CreateInstance (XamlType xamlType, Object [] args)
Meanwhile i have found another way.
I'm using a dummy-class called KeyAndValue:
/// <summary>
/// Dummy class with key and value properties for construction of comboboxitems.
/// </summary>
public class KeyAndValue
{
/// <summary>
/// Gets or sets the key.
/// </summary>
public string Key { get; set; }
/// <summary>
/// Gets or sets the value.
/// </summary>
public string Value { get; set; }
}
That class helps to fill the items-collection of the combobox:
foreach (System.Data.DataRow row in table.Rows)
{
// Add new pairs with Listkey and Listvalue as content to the Items-collection.
customComboBox.Items.Add(new KeyAndValue() { Key = row["Listkey"].ToString(), Value = row["Listvalue"].ToString() });
}
That's a little bit like using KeyValuePair with one diffenrence: A generic list like KeyValuePair cannot be serialized.
But my dummy-class does it.
What you need is to have a class with parameterless contrsuctor aka Default Constructor that will allow Serialization.
Step 1.
Create a class to hold your database results.
You can do this through manual code or you could use DataSet.
Step 2.
After you created the class or used the dataset serialize it.
FileStream fs = new FileStream(savePath + "\\" + a.Location + ".bin", FileMode.Create);
BinaryFormatter serializer = new BinaryFormatter();
try
{
serializer.Serialize(fs, a);
}
catch (Exception e)
{
//handle your fail;
}
finally
{
fs.Close();
}
or deserialize it:
List<Model.YOURCLASS> _l = new List<Model.YOURCLASS>();
string[] files = Directory.GetFiles(pathToSearch);
FileStream fs;
BinaryFormatter deserializer = new BinaryFormatter();
foreach (var a in files)
{
if(Path.GetExtension(a) == ".bin")
{
fs = new FileStream(a, FileMode.Open);
_l.Add((Model.YOURCLASS)deserializer.Deserialize(fs));
}
}
return _l;
The type that your routine returns doesn't have to be a DataTable your ListView will automatically use any collection you pass it, even an array[]

Argument not specified for parameter

Using VB.net & WPF
I've converted code available at Overlaying Controls in WPF with Adorners from C# to VB.Net
Original C# Code
/// <summary>
/// Overlays a control with the specified content
/// </summary>
/// <typeparam name="TOverlay">The type of content to create the overlay from</typeparam>
public class OverlayAdorner<TOverlay> : Adorner, IDisposable where TOverlay : UIElement, new()
{
private UIElement _adorningElement; private AdornerLayer _layer; /// <summary> /// Overlay the specified element /// </summary> /// <param name="elementToAdorn">The element to overlay</param> /// <returns></returns> public static IDisposable Overlay(UIElement elementToAdorn) { return Overlay(elementToAdorn, new TOverlay()); }
/// <summary>
/// Overlays the element with the specified instance of TOverlay
/// </summary>
/// <param name="elementToAdorn">Element to overlay</param>
/// <param name="adorningElement">The content of the overlay</param>
/// <returns></returns>
public static IDisposable Overlay(UIElement elementToAdorn, TOverlay adorningElement)
{
var adorner = new OverlayAdorner<TOverlay>(elementToAdorn, adorningElement);
adorner._layer = AdornerLayer.GetAdornerLayer(elementToAdorn);
adorner._layer.Add(adorner);
return adorner as IDisposable;
}
private OverlayAdorner(UIElement elementToAdorn, UIElement adorningElement)
: base(elementToAdorn)
{
this._adorningElement = adorningElement;
if (adorningElement != null)
{
AddVisualChild(adorningElement);
}
Focusable = true;
}
protected override int VisualChildrenCount
{
get { return _adorningElement == null ? 0 : 1; }
}
protected override Size ArrangeOverride(Size finalSize)
{
if (_adorningElement != null)
{
Point adorningPoint = new Point(0, 0);
_adorningElement.Arrange(new Rect(adorningPoint, this.AdornedElement.DesiredSize));
}
return finalSize;
}
protected override Visual GetVisualChild(int index)
{
if (index == 0 && _adorningElement != null)
{
return _adorningElement;
}
return base.GetVisualChild(index);
}
public void Dispose()
{
_layer.Remove(this);
}
}
VB.Net Code (Converted by Me)
Public Class OverlayAdorner(Of TOverlay As {UIElement, New})
Inherits Adorner
Implements IDisposable
Private _adorningElement As UIElement
Private _layer As AdornerLayer
Public Shared Function Overlay(elementToAdorn As UIElement, adorningElement As TOverlay) As IDisposable
Dim adorner = New OverlayAdorner(Of TOverlay)(elementToAdorn, adorningElement)
adorner._layer = AdornerLayer.GetAdornerLayer(elementToAdorn)
adorner._layer.Add(adorner)
Return TryCast(adorner, IDisposable)
End Function
Private Sub New(elementToAdorn As UIElement, adorningElement As UIElement)
MyBase.New(elementToAdorn)
Me._adorningElement = adorningElement
If adorningElement IsNot Nothing Then
AddVisualChild(adorningElement)
End If
Focusable = True
End Sub
Protected Overrides ReadOnly Property VisualChildrenCount() As Integer
Get
Return If(_adorningElement Is Nothing, 0, 1)
End Get
End Property
Protected Overrides Function ArrangeOverride(finalSize As Size) As Size
If _adorningElement IsNot Nothing Then
Dim adorningPoint As New Point(0, 0)
_adorningElement.Arrange(New Rect(adorningPoint, Me.AdornedElement.DesiredSize))
End If
Return finalSize
End Function
Protected Overrides Function GetVisualChild(index As Integer) As Visual
If index = 0 AndAlso _adorningElement IsNot Nothing Then
Return _adorningElement
End If
Return MyBase.GetVisualChild(index)
End Function
Public Sub Dispose() Implements IDisposable.Dispose
_layer.Remove(Me)
End Sub
End Class
Now I've created MainWindow & an User Control UserControl1 in my test project and trying code
Using OverlayAdorner(Of UserControl1).Overlay(G1)
'Err in First Line Itself
End Using
Error Argument not specified for parameter 'adorningElement' of 'Public Shared Function Overlay(elementToAdorn As System.Windows.UIElement, adorningElement As TOverlay) As System.IDisposable'.
What is Wrong Here
Below code block given on blog post you are referring to has incorrect usage shown.
using (OverlayAdorner<ProgressMessage>.Overlay(LayoutRoot))
{
// Do some stuff here while adorner is overlaid
}
Should be (in C#):
using (OverlayAdorner<ProgressMessage>.Overlay(LayoutRoot, this)) // Here I assume `this` is somehow `UserControl`'s object
{
// Do some stuff here while adorner is overlaid
}
In VB.NET
Using OverlayAdorner(Of UserControl).Overlay(G1, UserControl1)
' Do some stuff here while adorner is overlaid
End Using
Also note that: OverlayAdorner(Of UserControl1) should be OverlayAdorner(Of UserControl) because T here should be Type's name not name of object instance.
I hope that helps.
Some preaching
From this question of yours I get hint that you need to work yourself on language more. Give more time to VB.NET or C#.NET and use intellisense in Visual Studio to get yourself familiar with the class library and parameter types to be passed into methods you use. This will help you working in new class libraries which have some to no documentation.
I use this shortcut more when working in new class libaries. Type ctrl+k then ctrl+i. Generally said as ctrl+k,i
Update
Based on recent comment, Check the actual usage below:
XAML snippet:
<Window x:Class="WpfTestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid x:Name="G1">
<UserControl x:Name="ucMyControl"></UserControl>
</Grid>
</Window>
Code behind snippet:
Using OverlayAdorner(Of UserControl).Overlay(G1, ucMyControl)
' Do some stuff here while adorner is overlaid
End Using

Extracting a subset of DataTable columns in C# and then sending them to browser client as a JSON string

Here's the scenario: C# middle tier webservice has the data sent from SQL Server in a System.Data.DataTable object. There are dozens of columns in the DataTable's columns collection. The browser client only needs to see about six of them. The stored proc on the server is general purpose, not designed for this specific task. Assume we have to use it, rather than writing a new SP.
Is there a simple way, perhaps using generics, to extract the desired subset of columns from that DataTable into a List or a Collection, and then convert the resulting list or collection to JSON using JSON.NET?
Is it possible to create a skeletal C# class definition foo with the relevant field names (and datatypes) and then match on those field names "automagically" and thereby generate a List<foo> from the DataTable's rows collection?
Ideally, during the JSON conversion, any SQL Server datetime values (e.g. 2014-06-24T18:45:00) would be converted to a value that would make it easy to instantiate a javascript Date in the client without having to do string manipulation of the date representation.
Full working Console App code pasted below. But the 2 main methods you need are as follows.
For this code to work, you will have to do the following in your Project.
Add the JSON.Net Nuget Package to the Project.
Add a reference to System.Web.Extensions (if you get a compile error in the line where System.Web.Script.Serialization.JavaScriptSerializer is being referenced in the GetJson method.
/// <summary>
/// Returns Json representation of Generic class with only matching properties from the DataTable (passed as parameter)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dt"></param>
/// <returns></returns>
public static string GetJsonFromDataTable<T>(DataTable dt) where T : new()
{
string json = GetJson(dt);
return JsonConvert.SerializeObject(JsonConvert.DeserializeObject<List<T>>(json));
}
/// <summary>
/// Returns a JSON string for entire DataTable (passed as parameter)
/// </summary>
/// <param name="dt"></param>
/// <returns></returns>
public static string GetJson(DataTable dt)
{
System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
List<Dictionary<string, object>> rows = (from DataRow dr in dt.Rows select dt.Columns.Cast<DataColumn>().ToDictionary(col => col.ColumnName.Trim(), col => dr[col])).ToList();
return serializer.Serialize(rows);
}
Fully working Console App code.
Create a new console App, and replace everything in the Program.cs with this code. Also add JSON.Net to the Console App Project and add the references to System.Web.Extensions.
namespace DataTable2Json
{
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
public class Patient
{
public string FullName { get; set; }
public string PatientID { get; set; }
public int NumberOfIllnesses { get; set; }
public DateTime DateAdmitted { get; set; }
}
public class PatientDrug
{
public string Patient { get; set; }
public string Drug { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
DataTable patientDrugDataTable = GetPatientDrugTable();
DataTable patientDataTable = GetPatientTable();
string patientDrugJson = GetJsonFromDataTable<PatientDrug>(patientDrugDataTable);
Console.WriteLine("Json for PatientDrug:\n{0}",patientDrugJson);
string patientJson = GetJsonFromDataTable<Patient>(patientDataTable);
Console.WriteLine("\nJson for Patient:\n{0}", patientJson);
Console.WriteLine("\n\nPress a key to Exit...");
Console.ReadKey();
}
private static DataTable GetPatientDrugTable()
{
//
// Here we create a DataTable with four columns.
//
DataTable table = new DataTable();
table.Columns.Add("Dosage", typeof(int));
table.Columns.Add("Drug", typeof(string));
table.Columns.Add("Patient", typeof(string));
table.Columns.Add("Date", typeof(DateTime));
//
// Here we add five DataRows.
//
table.Rows.Add(25, "Indocin", "David", DateTime.Now);
table.Rows.Add(50, "Enebrel", "Sam", DateTime.Now);
table.Rows.Add(10, "Hydralazine", "Christoff", DateTime.Now);
table.Rows.Add(21, "Combivent", "Janet", DateTime.Now);
table.Rows.Add(100, "Dilantin", "Melanie", DateTime.Now);
return table;
}
private static DataTable GetPatientTable()
{
//
// Here we create a DataTable with four columns.
//
DataTable table = new DataTable();
table.Columns.Add("NumberOfIllnesses", typeof(int));
table.Columns.Add("PatientID", typeof(string));
table.Columns.Add("FullName", typeof(string));
table.Columns.Add("DateAdmitted", typeof(DateTime));
table.Columns.Add("StreetAddress1", typeof(string));
table.Columns.Add("City", typeof(string));
table.Columns.Add("State", typeof(string));
//
// Here we add five DataRows.
//
table.Rows.Add(2, "PAT-00001", "David", DateTime.Now, "1 Mill Ln", "Schenectady", "NY");
table.Rows.Add(1, "PAT-00002", "Sam", DateTime.Now, "1915 Boylston Steet", "Boston", "MA");
table.Rows.Add(3, "PAT-00003", "Christoff", DateTime.Now, "15 Polk Steet", "San Francisco", "CA");
table.Rows.Add(4, "PAT-00004", "Janet", DateTime.Now, "10 Waverly St", "Los Angeles", "CA");
table.Rows.Add(5, "PAT-00005", "Melanie", DateTime.Now, "50 Kapaa St", "Kailua", "HI");
return table;
}
/// <summary>
/// Returns Json representation of Generic class with only matching properties from the DataTable (passed as parameter)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dt"></param>
/// <returns></returns>
public static string GetJsonFromDataTable<T>(DataTable dt) where T : new()
{
string json = GetJson(dt);
return JsonConvert.SerializeObject(JsonConvert.DeserializeObject<List<T>>(json));
}
/// <summary>
/// Returns a JSON string for entire DataTable (passed as parameter)
/// </summary>
/// <param name="dt"></param>
/// <returns></returns>
public static string GetJson(DataTable dt)
{
System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
List<Dictionary<string, object>> rows = (from DataRow dr in dt.Rows select dt.Columns.Cast<DataColumn>().ToDictionary(col => col.ColumnName.Trim(), col => dr[col])).ToList();
return serializer.Serialize(rows);
}
}
}
Explanation of Code:
Notice I have 2 classes, Patient and PatientDrug.
I wrote helper methods to return data tables for both classes, that have additional columns.
Then the following 2 lines get the JSON for the class representation for Patient and PatientDrug respectively, while ignoring the additional data columns in DataTable that don't match names.
string patientDrugJson = GetJsonFromDataTable<PatientDrug>(patientDrugDataTable);
string patientJson = GetJsonFromDataTable<Patient>(patientDataTable);
Output in Console Windows (the json strings)

WPF Databinding to generic List<>

I am doing something wrong .. you know how it is.
I have tried playing around with ItemsSource , DataContext , DisplayMemberPath and SelectedValuePath and I either get a blank list of a list of the ToString method being called in the Person object;
WHAT WOULD REALLY HELP is for someone to publish an answer that works for this example.
I have simplified the problem as I am having difficulty in general with databinding generics.
I have created a simple Generic List of Person and want to bind it to a combo. (also want to try use a ListView too).
I either get a list of blanks or a list of 'xxxx.Person' where xxxx = namespace
<Window x:Class="BindingGenerics.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="300">
<Grid>
<ComboBox Name="ComboBox1"
ItemsSource="{Binding}"
Height="50"
DisplayMemberPath="Name"
SelectedValuePath="ID"
FontSize="14"
VerticalAlignment="Top">
</ComboBox>
</Grid>
</Window>
using System.Windows;
using System.ComponentModel;
namespace BindingGenerics
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Person p = new Person();
// I have tried List and BindingList
//List<Person> list = new List<Person>();
BindingList<Person> list = new BindingList<Person>();
p.Name = "aaaa";
p.ID = "1111";
list.Add(p);
p = new Person();
p.Name = "bbbb";
p.ID = "2222";
list.Add(p);
p = new Person();
p.Name = "cccc";
p.ID = "3333";
list.Add(p);
p = new Person();
p.Name = "dddd";
p.ID = "4444";
list.Add(p);
ComboBox1.DataContext = list;
}
}
public struct Person
{
public string Name;
public string ID;
}
}
In your code sample, Person.Name is a field rather than a property. WPF data binding considers only properties, not fields, so you need to change Person.Name to be a property.
Change your Person declaration to:
public class Person
{
public string Name { get; set; }
public string ID { get; set; }
}
(For production code, you'll probably want to use an ObservableCollection<Person> rather than a List<Person> and either make Person immutable or make it implement INotifyPropertyChanged -- but those aren't the sources of your immediate problem.)
In the code shown you're setting ItemsSource twice, the first time in XAML (called by InitializeComponent) to the DataContext of ComboBox1, which can't be determined from what you've posted but it's probably not what you want. After that you're resetting it from code to your list object (here with typos). In this code you're also adding the same instance of Person 4 times and just changing its Name and ID over and over. I suspect a combination of these issues and the fact that you're using a List instead of ObservableCollection are causing the issues in your application.
It would help narrow it down if you could post some actual code you're seeing problems with as what you've put here isn't even compilable.
Well... I'm assuming your actual code has corrected syntax, as the code you pasted in won't compile.
I put this code into a new WPF app and, after new-ing each Person object, my combobox populated fine. You might want to move your population code into a Loaded event, which will ensure the form is properly constructed. Here's the corrected xaml and codebehind (with a few syntax shortcuts):
xaml:
<Grid>
<ComboBox Name="ComboBox1" Height="70"
DisplayMemberPath="Name"
SelectedValuePath="ID" />
</Grid>
codebehind:
public Window1()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(Window1_Loaded);
}
void Window1_Loaded(object sender, RoutedEventArgs e)
{
var list = new List<Person>();
Person p = new Person(){Name = "aaaa",ID = "1111"};
list.Add(p);
p = new Person(){Name = "bbbb", ID="2222"};
list.Add(p);
p = new Person(){Name = "cccc", ID="3333"};
list.Add(p);
p = new Person(){Name = "dddd", ID="4444"};
list.Add(p);
ComboBox1.ItemsSource = list;
}

Resources