DataSet en Silverlight

septiembre 02, 2010 :: Posted by - Mario Andujar :: Category - , , , ,

Como muchos ya sabrán la clase DataSet en Silverlight no existe. No se porque Microsoft no incluyó esta clase en Silverlight, pero será supongo por tema de rapidez.

Navegando por la red encontré diversas maneras de trabajar con DataSet, pero la que mas me gusto es esta que explicare a continuación.

Nativamente no tenemos el objeto DataSet, pero si podemos pasarle una clase muy parecida desde el proyecto web.

[DataContract]

    public class DataColumnInfo

    {

        [DataMember]

        public string ColumnName { get; set; }

 

        [DataMember]

        public string ColumnTitle { get; set; }

 

        [DataMember]

        public string DataTypeName { get; set; }

 

        [DataMember]

        public bool IsRequired { get; set; }

 

        [DataMember]

        public bool IsKey { get; set; }

 

        [DataMember]

        public bool IsReadOnly { get; set; }

 

        [DataMember]

        public int DisplayIndex { get; set; }

 

        [DataMember]

        public string EditControlType { get; set; }

 

        [DataMember]

        public int MaxLength { get; set; }

    }

    [DataContract]

    public class DataTableInfo

    {

        [DataMember]

        public string TableName { get; set; }

 

        [DataMember]

        public List<DataColumnInfo> Columns { get; set; }

 

    }

 

    [DataContract]

    public class DataSetData

    {

        [DataMember]

        public List<DataTableInfo> Tables { get; set; }

        [DataMember]

        public string DataXML { get; set; }

 

        public static DataSetData FromDataSet(DataSet ds)

        {

            DataSetData dsd = new DataSetData();

            dsd.Tables = new List<DataTableInfo>();

            foreach (DataTable t in ds.Tables)

            {

                DataTableInfo tableInfo = new DataTableInfo { TableName = t.TableName };

                dsd.Tables.Add(tableInfo);

                tableInfo.Columns = new List<DataColumnInfo>();

                foreach (DataColumn c in t.Columns)

                {

                    DataColumnInfo col = new DataColumnInfo { ColumnName = c.ColumnName, ColumnTitle = c.ColumnName, DataTypeName = c.DataType.FullName, MaxLength = c.MaxLength, IsKey = c.Unique, IsReadOnly = (c.Unique || c.ReadOnly), IsRequired = !c.AllowDBNull };

                    if (c.DataType == typeof(System.Guid))

                    {

                        col.IsReadOnly = true;

                        col.DisplayIndex = -1;

                    }

                    tableInfo.Columns.Add(col);

               }

            }

            dsd.DataXML = ds.GetXml();

            return dsd;

        }

 

        public static DataSet ToDataSet(DataSetData dsd)

        {

            DataSet ds = new DataSet();

            UTF8Encoding encoding = new UTF8Encoding();

            Byte[] byteArray = encoding.GetBytes(dsd.DataXML);

            MemoryStream stream = new MemoryStream(byteArray);

            XmlReader reader = new XmlTextReader(stream);

            ds.ReadXml(reader);

            XDocument xd = XDocument.Parse(dsd.DataXML);

            foreach (DataTable dt in ds.Tables)

            {

                var rs = from row in xd.Descendants(dt.TableName)

                         select row;

 

                int i = 0;

                foreach (var r in rs)

                {

                    DataRowState state = (DataRowState)Enum.Parse(typeof(DataRowState), r.Attribute("RowState").Value);

                    DataRow dr = dt.Rows[i];

                    dr.AcceptChanges();

                    if (state == DataRowState.Deleted)

                        dr.Delete();

                    else if (state == DataRowState.Added)

                        dr.SetAdded();

                    else if (state == DataRowState.Modified)

                       dr.SetModified();

                    i++;

                }

            }

            return ds;

        }

    }

Lo que debemos hacer es trabajar con el objeto DataSet desde el proyecto web y en el momento que queramos pasarle el DataSet a Silverlight deberemos pasar el objeto DataSetData de esta manera:

public static DataSetData CreateDataSet( DataSet dataSet )

{

    return DataSetData.FromDataSet(dataSet);

}

Después para trabajar con este objeto en Silverlight es muy sencillo. Por ejemplo vamos a pasar los datos del DataSet a un DataGrid. Primero deberemos crear estas clases.

public class DynamicDataBuilder

    {

        private static System.Type BuildDataObjectType(DataColumnInfo[] Columns, string DataObjectName)

        {

            AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("AprimoDynamicData"), AssemblyBuilderAccess.Run);

            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DataModule");

 

            TypeBuilder tb = moduleBuilder.DefineType(DataObjectName,

                                                    TypeAttributes.Public |

                                                    TypeAttributes.Class,

                                                    typeof(DataObject));

 

            ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

            foreach (var col in Columns)

            {

                string propertyName = col.ColumnName.Replace(' ', '_');

                Type dataType = System.Type.GetType(col.DataTypeName, false, true);

                if (dataType != null)

                {

                    FieldBuilder fb = tb.DefineField("_" + propertyName, dataType, FieldAttributes.Private);

                    PropertyBuilder pb = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, dataType, null);

                    MethodBuilder getMethod = tb.DefineMethod("get_" + propertyName,

                                                                MethodAttributes.Public |

                                                                MethodAttributes.HideBySig |

                                                                MethodAttributes.SpecialName,

                                                                dataType,

                                                                Type.EmptyTypes);

 

                    ILGenerator ilgen = getMethod.GetILGenerator();

                    //Emit Get property, return _prop

                    ilgen.Emit(OpCodes.Ldarg_0);

                    ilgen.Emit(OpCodes.Ldfld, fb);

                    ilgen.Emit(OpCodes.Ret);

                    pb.SetGetMethod(getMethod);

                    MethodBuilder setMethod = tb.DefineMethod("set_" + propertyName,

                    MethodAttributes.Public |

                    MethodAttributes.HideBySig |

                    MethodAttributes.SpecialName,

                    null,

                    new Type[] { dataType });

                    ilgen = setMethod.GetILGenerator();

                    LocalBuilder localBuilder = ilgen.DeclareLocal(typeof(String[]));

                    //Emit set property, _Prop = value;

                    ilgen.Emit(OpCodes.Ldarg_0);

                    ilgen.Emit(OpCodes.Ldarg_1);

                    ilgen.Emit(OpCodes.Stfld, fb);

 

                    //Notify Change:

                    Type[] wlParams = new Type[] { typeof(string[]) };

                    MethodInfo notifyMI = typeof(DataObject).GetMethod("NotifyChange",

                    BindingFlags.NonPublic |

                    BindingFlags.Instance,

                    null,

                    CallingConventions.HasThis,

                    wlParams,

                    null);

 

                    //NotifyChange Property change

                    ilgen.Emit(OpCodes.Ldc_I4_1);

                    ilgen.Emit(OpCodes.Newarr, typeof(String));

                    ilgen.Emit(OpCodes.Stloc_0);

                    ilgen.Emit(OpCodes.Ldloc_0);

                    ilgen.Emit(OpCodes.Ldc_I4_0);

                    ilgen.Emit(OpCodes.Ldstr, propertyName);

                    ilgen.Emit(OpCodes.Stelem_Ref);

                    ilgen.Emit(OpCodes.Ldarg_0);

                    ilgen.Emit(OpCodes.Ldloc_0);

                    ilgen.EmitCall(OpCodes.Call, notifyMI, null); // call nodifyChange function

 

                    ilgen.Emit(OpCodes.Ret);

                    pb.SetSetMethod(setMethod);

                }

            }

            System.Type rowType = tb.CreateType();

            //assemblyBuilder.Save("DynamicData.dll");

            return rowType;

        }

 

        public static IEnumerable GetDataList(DataSetData data)

        {

            if (data.Tables.Count() == 0)

                return null;

 

            DataTableInfo tableInfo = data.Tables[0];

            //string datatype = data.Tables.GetType().ToString();

            //string datatype2 = data.DataXML.GetType().ToString();

            System.Type dataType = BuildDataObjectType(tableInfo.Columns, tableInfo.Columns.GetType().ToString());

 

            //ObservableCollection<DataObject> l = new ObservableCollection<DataObject>();

 

            var listType = typeof(ObservableCollection<>).MakeGenericType(new[] { dataType });

            var list = Activator.CreateInstance(listType);

 

            XDocument xd = XDocument.Parse(data.DataXML);

            var table = from row in xd.Descendants(tableInfo.TableName)

                        select row.Elements().ToDictionary(r => r.Name, r => r.Value);

 

            foreach (var r in table)

            {

                var rowData = Activator.CreateInstance(dataType) as DataObject;

                if (rowData != null)

                {

                    foreach (DataColumnInfo col in tableInfo.Columns)

                    {

                        if (r.ContainsKey(col.ColumnName) && col.DataTypeName != typeof(System.Byte[]).FullName && col.DataTypeName != typeof(System.Guid).FullName)

                            rowData.SetFieldValue(col.ColumnName, r[col.ColumnName], true);

                    }

                }

                listType.GetMethod("Add").Invoke(list, new[] { rowData });

            }

            ObservableCollection<DataObject> l = list as ObservableCollection<DataObject>;

            return list as IEnumerable;

        }

 

        public static DataSetData GetUpdatedDataSet(IEnumerable list, DataTableInfo[] tables)

        {

            DataSetData data = new DataSetData();

            data.Tables = tables;

            //data.Tables = new ObservableCollection<DataSetInDataGrid.Silverlight.MyDataService.DataTableInfo>();

            //foreach (MyDataService.DataTableInfo t in tables)

            //{

            //    MyDataService.DataTableInfo table = new MyDataService.DataTableInfo { TableName = t.TableName };

            //    table.Columns = new ObservableCollection<DataSetInDataGrid.Silverlight.MyDataService.DataColumnInfo>();

            //    foreach (MyDataService.DataColumnInfo c in t.Columns)

            //    {

            //        table.Columns.Add(new MyDataService.DataColumnInfo{ColumnName= c.ColumnName, DataTypeName

            //    }

            //}

 

            XElement root = new XElement("DataSet");

            foreach (DataObject d in list)

            {

                if (d.State != DataObject.DataStates.Unchanged)

                {

                    XElement row = new XElement("Data", new XAttribute("RowState", d.State.ToString()));

                    PropertyInfo[] pis = d.GetType().GetProperties();

                    foreach (PropertyInfo pi in pis)

                    {

                        object val = pi.GetValue(d, null);

                        if (val != null)

                            row.Add(new XElement(pi.Name, val.ToString()));

                        else

                            row.Add(new XElement(pi.Name, ""));

                    }

                    root.Add(row);

                }

            }

            XDocument xdoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"), root);

            data.DataXML = xdoc.ToString();

 

            return data;

        }

    }

    public class DataObject : INotifyPropertyChanged, IEditableObject

    {

        protected Dictionary<string, object> _backupData = new Dictionary<string, object>();

 

        public enum DataStates

        {

            Unchanged = 2,

            Added = 4,

            Deleted = 8,

            Modified = 16,

        }

        public DataStates State { get; set; }

        public object GetFieldValue(string fieldname)

        {

            PropertyInfo pi = this.GetType().GetProperty(fieldname);

            if (pi != null)

                return pi.GetValue(this, null);

            return null;

        }

        public void SetFieldValue(string fieldname, object value, bool initial)

        {

            this.SetFieldValue(fieldname, value);

            if (initial)

            {

                this.State = DataStates.Unchanged;

                if (!_backupData.ContainsKey(fieldname))

                    _backupData.Add(fieldname, value);

            }

 

        }

        public void SetFieldValue(string fieldname, object value)

        {

            PropertyInfo pi = this.GetType().GetProperty(fieldname);

            if (pi != null)

            {

                object pValue = Convert.ChangeType(value, pi.PropertyType, null);

                if (pValue != null)

                    pi.SetValue(this, pValue, null);

            }

        }

        public void Delete()

        {

            this.State = DataStates.Deleted;

        }

        public void NewRow()

        {

            this.State = DataStates.Added;

        }

        protected void NotifyChange(params string[] properties)

        {

            if (PropertyChanged != null)

            {

                foreach (string p in properties)

                    PropertyChanged(this, new PropertyChangedEventArgs(p));

                this.State = DataStates.Modified;

            }

        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

 

        #region IEditableObject Members

 

        public void BeginEdit()

        {

        }

 

        public void CancelEdit()

        {

            foreach (string fieldName in _backupData.Keys)

                this.SetFieldValue(fieldName, _backupData[fieldName], true);

        }

 

        public void EndEdit()

        {

 

        }

 

        #endregion

    }

Finalmente pasamos los datos al DataGrid.

dataGrid.ItemsSource = null;

dataGrid.Columns.Clear();

dataGrid.ItemsSource = DynamicDataBuilder.GetDataList(DataSetData);

Espero que os sirva :)

Control Silverlight RichtTextBox

julio 21, 2010 :: Posted by - Mario Andujar :: Category - , , ,

image

El otro día en el trabajo me pidieron si podía crear dos controles nuevos en Silverlight para poder introducir texto formateado y poder visionarlo.

Buscando por la red encontré una opción gratuita bastante buena para lo que me pedían. El control en si se encuentra en http://www.vectorlight.net/demos/richtextbox.aspx y es un editor de texto bastante completo (tiene incluso un diccionario ortográfico para corregir errores de escritura y agregar nuevas definiciones). Solo con cambiar y poner las cuatro opciones que me pedían ya quedaron realizados estos dos controles.

Capturar evento click derecho con SilverLight

marzo 17, 2010 :: Posted by - Emilio Torrens :: Category - , , ,

Aquí vemos como capturar el evento del botón derecho en silverlight.

Este evento desde la salida de silverlight ha estado inactivo, y dicen que en la siguiente versión, silverlight 4, estará disponible (también dijeron eso en la versión 3).

Cuando haces click derecho en la aplicación Silverlight te muestra un menú contextual y un dialogo de configuración.

¿Y si quieres usar el botón derecho en tu aplicación silverlight? El proceso es muy sencillo, y aquí os detallare unos pasos que deberéis de seguir para poder utilizar dicho evento y mostrar un menú contextual propio.

Paso 1: Tenemos que establecer el control de Silverlight para ventanas. En la pagina web del proyecto (suele ser default.aspx) que contiene el control Silverlight añadir la etiquetawindowless=”true”.

Paso 2: Introducimos el contextmenu donde queramos capturar el evento click derecho. Dentro del xaml creamos una nueva clase llamada ContextMenuInterceptor. En el constructor de esta clase capturamos un evento llamado “OnContextMenu” con HtmlPage.Document.AttachEvent (deberás agregar la referencia System.Window.Browser).

Dentro de este evento llamamos a e.PeventDefault(). Esto cancela la propagación del evento click derecho para que silverlight no lo reciba (así no mostrará su menu contextual).

En este punto, es cuando usted esta capturando el evento y podra mostrar o hacer cualquier cosa. En el ejemplo que pongo a continuación utilizo yo un TextBlock para mostrar cuando capturamos el click derecho.

Page.xml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Browser;

namespace SilverlightApplication15
{
    public partial class Page : UserControl
    {
        ContextMenuInterceptor _cmi = null;
        public Page()
        {
            InitializeComponent();
            _cmi = new ContextMenuInterceptor(MyField);
        }
    }       

    public class ContextMenuInterceptor
    {
        TextBlock TextField;

        public ContextMenuInterceptor(TextBlock textField)
        {
            TextField = textField;
            HtmlPage.Document.AttachEvent("oncontextmenu", this.OnContextMenu);
        }

        private void OnContextMenu(object sender, HtmlEventArgs e)
        {
            TextField.Text = "Right Clicked Blocked at "+e.OffsetX+","+e.OffsetY;
            e.PreventDefault();
        }
    }
}

MIX10

enero 14, 2010 :: Posted by - Emilio Torrens :: Category - , , ,

clip_image001Me acaba de llegar el mail de Microsoft anunciando que ya te puedes inscribir en el MIX10 de Las Vegas.

MIX es el evento mas orientado a la experiencia de usuario en la web:

“Diseñadores y desarrolladores aprenderán todo sobre la amplia plataforma web de Microsoft y sus herramientas, que ayudan a reunir clientes, servidores y servicios en la nube, necesarios para crear grandes aplicaciones web ricas.â€

600 dólares de descuento y una noche gratis de hotel si te registras antes del 15 de Enero.

Aquí puedes ver las  sesiones y aquí los ponentes

clip_image001

Silverlight 4.0 Beta ya disponible

noviembre 20, 2009 :: Posted by - Emilio Torrens :: Category -

microsoft_silverlight Ayer se lanzo en el PDC la versión Beta de Silverlight 4.0, ósea que desde ya tenemos todo lo necesario para trabajar con la nueva versión en el sitio de SilverLight.

Para enterarte mas a fondo de las nuevas funcionalidades puedes visitar el sitio del PDC y ver las sesiones sobre el tema :)

Para empezar a probar necesitas tener instalado Visual Studio 2010 Beta o el Visual Web Developer Express 2010 Beta y aquí encontraras la documentación online.

A disfrutarlo :)

Lanzamiento Expression 3

julio 24, 2009 :: Posted by - Emilio Torrens :: Category - , ,

He leído el el Blog de Mike Ormond que ayer anunciaron en un evento de UK el lanzamiento de Expression 3, la versión Web viene con soporte para CSS, HTML, ASP.NET, PHP y Deep Zoom, Silverlight y Flash, podéis ver una demo en este video,

 

Mas info en el blog de expresión los detalles y bajarse las demos desde la web de expresión

Preview de Moonlight 2

mayo 06, 2009 :: Posted by - Emilio Torrens :: Category - ,

moonlight_logo Moonlight es la implementación de Silverlight para Mono, o lo que es lo mismo, es lo que permitirá a los usuarios de Linux correr nuestras aplicaciones Silverlight.

Bueno pues en el blog de Mary Jo Foley se anuncia que la preview de la versión 2 esta disponible para los Testers.

Es una muy buena noticia para los que programamos Silverlight ya que eso hará que nuestras aplicaciones puedan correr en todas las plataformas, además he leído que habrá un Moonlight 3, así  que estaremos atentos.

Ya disponible Silverlight 2 Release Candidate

septiembre 29, 2008 :: Posted by - Emilio Torrens :: Category -

Ya esta disponible la release candidate 0 de silverlight 2. Podéis ver un resumen de las novedades en este post de Scott Guthrie

Y aquí tenéis una lista de los Breaking Changes y como arreglarlos.

Silverlight maxJsonLength

noviembre 15, 2007 :: Posted by - Emilio Torrens :: Category -

Haciendo una llamada a un servicio Web desde Silverlight me encontré que en determinados casos recibía "Error invoking service".

Gracias a tan descriptivo error me fue "sencillísimo" encontrar información sobre el error, la causa era la longitud máxima por defecto de las cadenas JSON. Solución: añadir el siguiente fragmento XML justo después de  system.web

</system.web>.

<system.web.extensions>
    <scripting>
      <webServices>
        <jsonSerialization maxJsonLength="2097152"/>
      </webServices>
    </scripting>
</system.web.extensions>

20971520 es el valor máximo permitido, que vienen a ser unos 4 megas.

Nuevo libro Silverlight 1.0

octubre 21, 2007 :: Posted by - Emilio Torrens :: Category - , , ,

Nuevo libro a todo color sobre esta nueva tecnología web multiplataforma.

   

Silverlight 1.0 (Wrox)
Devin Rader, Jason Beres, J. Ambrose Little, Grant Hinkson

ISBN: 978-0-470-22840-1
October 2007
320 páginas

Entre los temas tratados en el libro:

• Incorporar gráficos, animaciones, videos y sonidos en nuestras aplicaciones web.

• Como programar con Java Script cualquier elemento XAML DOM.

• Como integrar Silverlight con páginas HTML y aplicaciones ASP.NET existentes.

• Y como manejar el acceso a datos en Silverlight con AJAX.