Blogs

The One With

  • XtraGrid - Binding a Grid to Data (Transcript)

    http://tv.devexpress.com/XtraGrid01.movie

    Download Transcript: PDF Word XPS

     

    In this lesson, our main goal is to show you how to bind XtraGrid to data. Thus, I’m going to create an extremely simple application that has a single form with Grid Control bound to an access database. 

     

    1. First, let’s drop a Grid Control onto a new form.

    2. And make it fill the entire form’s area.

    3. Now we need to create a data source and bind it to the grid.

    4. These actions can be done at once by the Grid’s smart tag.

    5. I click the “Add Project Data Source…” link to start the “Data Source Configuration Wizard”.

    6. The first step is to choose a data source type and it is “Database” in our case.


    7. Hence, I leave the default option and proceed to the next page to choose a data connection.

    8. I get to create a new connection here.

    9. All I have to do is to locate the desired database file on the disk . . . and I’m done!


    10. In the last step, I’ll choose the data table that will supply data to the grid.

    11. The “Customers” table will do nicely in this lesson.

    12. I’ll specify a meaningful name for the DataSet and click Finish.


    14. The wizard has been completed . . . AND . . . as you can see, I have not only created the Data Source, but also bound it to the grid.

    15. We can see that the grid has automatically retrieved all the fields from the bound table and created columns for them.

    16. Since I don’t need to display all the columns, I’ll invoke the Grid’s Designer to remove some of them.

    17. I’ll switch to the columns page, select the columns I wish to remove, and delete them with a single click.


    18. You see that the corresponding field names are now bold, indicating that there are no columns bound to them.

    19. To create a column bound to a particular field, you can simply drag an item from the Fields ListBox, to the Columns ListBox.

    20. Now I’m done with column customization, so I’ll close the designer.

    21. Finally, I can run the application to see the result.

    22. A Grid Control, filled with data from the bound Data Table.

  • DevExpress Channel is growing

    Dear DXperiencers,

    In an effort to bring you as much educational material as possible, back on July 29 we launched the DevExpress Channel :).

    A portal site where we have been and are going to continue broadcasting technical and non-technical videos related to DevExpress products and technologies in general.

    You'll be happy to know that the Channel is growing :) So please welcome our newest member Amanda, and stay tuned for more more more and more videos.

    Watch the video: http://tv.devexpress.com/XtraScheduler01.movie

    View the transcript: Word PDF XPS

    Cheers

    Azret

  • Spell Checker : Advanced Features

    image

    http://tv.devexpress.com/XtraSpellChecker02.movie

    The second video in the XtraSpellChecker tutorial series shows you how to use certain advanced features such as CheckAsYouType.

  • Spell Checker : Getting Started

    In this video, Erica demonstrates how to add the spell checking capabilities to an application using the XtraSpellChecker library.

    Spell Checker 101: Getting Started

    http://tv.devexpress.com/XtraSpellChecker01.movie

    Cheers

    Azret

  • Silverlight and Data Access using XPO

    It used to be that in any thin client application that you build, you would spent most time worrying about how to bring your data to the client and then how to properly submit your changes back. These days, as the data access frameworks and the ORM solutions matured, you can actually afford to think about your application, your business logic etc... instead of constantly tinkering with the DA plumbing.

    Such challenges still exist, to a certain degree, for young platforms like Silverlight. But you'd be happy to know that XPO is making it to Silverlight. In fact, you can already find some bits in your v8.2 install :).

    I am going to show you how to use them... [If you are new to XPO I suggest reading more about it here http://www.devexpress.com/Products/NET/ORM/ and also check out Gary's Blog for some XPO related goodies...]

    Warning: These are pre-release preview bits for (DXperience 8.2.4) and are subject to change. 

    Developing an HTTP Data Layer

    To accomplish the flow illustrated above, we will need an IDataStore that will work over HTTP.

    public interface IDataStore {
    AutoCreateOption AutoCreateOption { get; }
    ModificationResult ModifyData(params ModificationStatement[] dmlStatements);
    SelectedData SelectData(params SelectStatement[] selects);
    UpdateSchemaResult UpdateSchema(bool dontCreateIfFirstTableNotExist, params DBTable[] tables);
    }

    On the server side, we will expose it as a WCF Web Service.

    [XmlSerializerFormat]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    [ServiceContract(Namespace = "")]
    public class Gateway {
    static IDataStore s_dataStore;

    static Gateway() {
    s_dataStore = XpoDefault.GetConnectionProvider(
    MSSqlConnectionProvider.GetConnectionString("<COMPUTER_NAME>", "<DATABASE_NAME>"),
    AutoCreateOption.DatabaseAndSchema);
    }

    [OperationContract]
    public AutoCreateOption GetAutoCreateOption() {
    return s_dataStore.AutoCreateOption;
    }

    [OperationContract]
    public SelectedData SelectData(SelectStatement[] selects) {
    if (selects != null && selects.Length > 0 && selects[0].TableName == "XPObjectType") {
    return new SelectedData(new SelectStatementResult());
    }
    return s_dataStore.SelectData(selects);
    }

    [OperationContract]
    public UpdateSchemaResult UpdateSchema(bool dontCreateIfFirstTableNotExist,
    DBTable[] tables) {
    return s_dataStore.UpdateSchema(dontCreateIfFirstTableNotExist, tables);
    }

    [OperationContract]
    public ModificationResult ModifyData(ModificationStatement[] statements) {
    return s_dataStore.ModifyData(statements);
    }
    }

     

    And on the client side we will create a wrapper for it.

    The contract:

    [XmlSerializerFormat]
    [ServiceContractAttribute(Namespace = "")]
    public interface IGateway {
    [OperationContractAttribute(AsyncPattern = true,
    Action = "urn:Gateway/GetAutoCreateOption", ReplyAction = "urn:Gateway/GetAutoCreateOptionResponse")]
    IAsyncResult BeginGetAutoCreateOption(AsyncCallback callback, object asyncState);
    AutoCreateOption EndGetAutoCreateOption(IAsyncResult result);

    [OperationContractAttribute(AsyncPattern = true,
    Action = "urn:Gateway/SelectData", ReplyAction = "urn:Gateway/SelectDataResponse")]
    IAsyncResult BeginSelectData(SelectStatement[] selects, AsyncCallback callback, object asyncState);
    SelectedData EndSelectData(IAsyncResult result);

    [OperationContract(AsyncPattern = true,
    Action = "urn:Gateway/UpdateSchema", ReplyAction = "urn:Gateway/UpdateSchemaResponse")]
    IAsyncResult BeginUpdateSchema(bool dontCreateIfFirstTableNotExist, DBTable[] tables, AsyncCallback callback, object asyncState);
    UpdateSchemaResult EndUpdateSchema(IAsyncResult result);

    [OperationContract(AsyncPattern = true,
    Action = "urn:Gateway/ModifyData", ReplyAction = "urn:Gateway/ModifyDataResponse")]
    IAsyncResult BeginModifyData(ModificationStatement[] statements, AsyncCallback callback, object asyncState);
    ModificationResult EndModifyData(IAsyncResult result);
    }

    And the transport channel:

    public class Gateway : ClientBase<IGateway>, IGateway, IDataStore {
    #region IDataStore Members
    ...
    public SelectedData SelectData(params SelectStatement[] selects) {
    if (this._dispatcher.CheckAccess()) {
    throw new InvalidOperationException();
    }

    var _ar = BeginSelectData(selects, null, null);

    if (!_ar.IsCompleted) {
    _ar.AsyncWaitHandle.WaitOne();
    }

    return EndSelectData(_ar);
    }
    ...
    #endregion

    #region IGateway Memebers
    ...
    public IAsyncResult BeginSelectData(SelectStatement[] selects, AsyncCallback callback, object asyncState) {
    return base.Channel.BeginSelectData(selects, callback, asyncState);
    }

    public SelectedData EndSelectData(IAsyncResult result) {
    return base.Channel.EndSelectData(result);
    }
    ...
    #endregion

    #region ClientChannel
    private class ClientChannel : ChannelBase<IGateway>, IGateway {
    ...
    public IAsyncResult BeginSelectData(SelectStatement[] selects, AsyncCallback callback, object asyncState) {
    object[] _args = new object[1];
    _args[0] = selects;
    IAsyncResult _result = base.BeginInvoke("SelectData", _args, callback, asyncState);
    return _result;
    }
    public SelectedData EndSelectData(IAsyncResult result) {
    object[] _args = new object[0];
    SelectedData _result = (SelectedData)base.EndInvoke("SelectData", _args, result);
    return _result;
    }
    ...
    }
    #endregion
    }

    Full implementation of the Gateway proxy can be downloaded here: Gateway.cs

     

    Preparing your Silverlight Application

    In your Silverlight project add references to the following assemblies:

    • DevExpress.Data.v8.2.SL
    • DevExpress.Xpo.v8.2.SL
    • System.Xml.Serialization
    • System.ServiceModel

    Add the Gateway.cs and include a config. file for the gateway web service:

    <configuration>
    <system.serviceModel>
    <bindings>
    <basicHttpBinding>
    <binding name="BasicHttpBinding_Gateway" maxBufferSize="65536"
    maxReceivedMessageSize="65536">
    <security mode="None" />
    </binding>
    </basicHttpBinding>
    </bindings>
    <client>
    <endpoint address="http://localhost:50691/Gateway.svc" binding="basicHttpBinding"
    bindingConfiguration="BasicHttpBinding_Gateway" contract="DevExpress.Xpo.Xtras.IGateway"
    name="BasicHttpBinding_Gateway" />
    </client>
    </system.serviceModel>
    </configuration>

     

    Working with Data

    Now that all the prop. work is out of the way, we can start executing some queries. Suppose you are building an auto showroom application and you want to retrieve all the BMWs from the table inventory.

    [OptimisticLocking(false)]
    public class Inventory : PersistentBase {
    Guid _id;
    [Key(AutoGenerate = true)]
    public Guid ID {
    get { return _id; }
    set { SetPropertyValue("ID", ref _id, value); }
    }

    string _make;
    public string Make {
    get { return _make; }
    set { SetPropertyValue("Make", ref _make, value); }
    }

    string _model;
    public string Model {
    get { return _model; }
    set { SetPropertyValue("Model", ref _model, value); }
    }

    public Inventory(Session session)
    : base(session) {
    }
    }
     

    UnitOfWork unitOfWork = new UnitOfWork(new SimpleDataLayer(_gateway));

    XPQuery<Inventory> inventory = new XPQuery<Inventory>(unitOfWork);
    var query = from t in inventory
    where t.Make == "BMW"
    select t;

    var list = query.ToList<Inventory>();

    // Do stuff with the list... bind it to a grid etc...

     

    Where _gateway is an instance of our DevExpress.Xpo.Xtras.Gateway : IDataStore initialized somewhere during startup or page load.

    Modifying or creating a new record is also done naturally via the Inventory class.

    using (UnitOfWork u = new UnitOfWork(new SimpleDataLayer(this._gateway))) {
    var o = new Inventory(u);
    o.Make = "Audi";
    u.Save(o);
    u.CommitChanges();
    }

    One very important thing to remember here is that all operations that require a web service call need to be performed asynchronously. This is enforced by the Silverlight runtime [and it's good thing]. So always invoke your calls from a worker thread, for example:

    ThreadPool.QueueUserWorkItem(delegate(object state) {

    UnitOfWork unitOfWork = new UnitOfWork(new SimpleDataLayer(_gateway));

    XPQuery<Inventory> inventory = new XPQuery<Inventory>(unitOfWork);
    var query = from t in inventory
    where t.Make == "BMW"
    select t;

    var list = query.ToList<Inventory>();

    Dispatcher.BeginInvoke(() => {
    agDataGrid.DataSource = list;
    });

    });

    Download Sample Project

    Cheers,

    Azret

  • Custom Silverlight Controls: Creating a reusable MessageBox dialog. (Part III)

    We have created the basics for the MessageBox dialog in Part I and Part II. One thing that we realized is that there is no way to block on MessageBox.Show. The cleanest solution to get the dialog result is to make our call API "asynchronous".

    Something like this:

    public delegate void DialogCloseDelegate(
    object sender,
    DialogResult dialogResult);
     
    public static class MessageBox {
    public static void Show(string title, string text, DialogStyle dialogStyle,
    DialogCloseDelegate callback) {
    InternalShow(title, text, dialogStyle, callback);
    }
    }
    ...
    public class MessageBoxControl : ContentControl {
    DialogCloseDelegate _callback;

    public MessageBoxControl(Popup owner, DialogCloseDelegate callback)
    : this() {
    this._callback = callback;
    ...
    }

    void DialogButtonClick(object sender, RoutedEventArgs e) {
    DialogButton button = sender as DialogButton;
    if (this._owner != null && button != null) {
    this._owner.IsOpen = false;
    if (_callback != null) {
    _callback(this, button.DialogResult);
    }
    }
    }
    }

    We can then use the MessageBox like so:

    private void Button_Click(object sender, RoutedEventArgs e) {
    MessageBox.Show("Title", "Hello World", DialogStyle.OkAndCancel,
    new DialogCloseDelegate(OnDialogClose));
    }

    void OnDialogClose(object sender, DialogResult dialogResult) {
    MessageBox.Show("Dialog Result", dialogResult.ToString());
    }
     
     
    What's Next?

    Since this is just demo code, a lot of things have been simplified. For example you might want to move the hard-coded "OK" and "Cancel" button contents out into properties. You could make the default template for the message box surface a little prettier, so that it has the look and feel of your application. Hopefully, this article will get you started.

    Download the Source Code

    Cheers,

    Azret

  • Custom Silverlight Controls: Creating a reusable MessageBox dialog. (Part II - The Popup)

    We started to build a generic MessageBox in the previous article and we started by creating a simple MessageBox content control (the surface for our dialogs). This time, we will be putting things together to actually show the dialog as a popup.

    The basic idea for the MessageBox.Show is to create a System.Windows.Controls.Primitives.Popup and place the dialog surface inside of it.

    public static class MessageBox {
    public static DialogResult Show(string title, string text, DialogStyle dialogStyle) {
    Popup popup = new Popup();
    MessageBoxControl surface = new MessageBoxControl(popup);
    surface.HeaderContent = title;
    surface.Content = text;
    surface.DialogStyle = dialogStyle;
    surface.Width = 200;
    surface.Height = 140;
    popup.Child = surface;
    popup.IsOpen = true;
    return DialogResult.None;
    }
    }

    Updated MessageBoxControl::DialogButtonClick:

    void DialogButtonClick(object sender, RoutedEventArgs e) {
    DialogButton button = sender as DialogButton;
    if (this._owner != null && button != null) {
    this._owner.IsOpen = false;
    }
    }

    One thing you will notice with this is that it does not give you the modal feel. In other words, we are still able to click outside of the MessageBox. We can solve this by placing a transparent container that will cover the entire client area before displaying the dialog. A Canvas is the best option in this case as it would allow us to implement window dragging later on.

    public static DialogResult Show(string title, string text, DialogStyle dialogStyle) {
    Popup popup = new Popup();

    Canvas backgroundPanel = new Canvas();
    // Transparent background for our hidden panel
    backgroundPanel.Background = new SolidColorBrush(Colors.Transparent);
    // Make it fill up the whole client area
    backgroundPanel.Width = Application.Current.Host.Content.ActualWidth;
    backgroundPanel.Height = Application.Current.Host.Content.ActualHeight;

    MessageBoxControl surface = CreateDialogSurface(title, text, dialogStyle, popup);
    backgroundPanel.Children.Add(surface);
    CenterControl(surface, backgroundPanel);

    popup.Child = backgroundPanel;
    popup.IsOpen = true;
    return DialogResult.None;
    }
     
    private static MessageBoxControl CreateDialogSurface(string title, string text, DialogStyle dialogStyle, Popup popup) {
    MessageBoxControl surface = new MessageBoxControl(popup);
    surface.HeaderContent = title;
    surface.Content = text;
    surface.DialogStyle = dialogStyle;
    surface.Width = 200;
    surface.Height = 140;
    surface.Background = new SolidColorBrush(Colors.White);
    return surface;
    }

    private static void CenterControl(FrameworkElement element, Canvas parent) {
    if (element.ActualHeight == 0 || element.ActualHeight == double.NaN){
    element.Measure(new Size(parent.ActualWidth, parent.ActualHeight));
    }
    element.SetValue(Canvas.TopProperty, Math.Ceiling(parent.ActualHeight / 2 - element.DesiredSize.Height / 2));
    element.SetValue(Canvas.LeftProperty, Math.Ceiling(parent.ActualWidth / 2 - element.DesiredSize.Width / 2));
    }

    This is what we have have so far:

    Page.xaml.cs:

    private void Button_Click(object sender, RoutedEventArgs e) {
    DevExpressTV.Forms.MessageBox.Show("Title", "Hello World", DevExpressTV.Forms.DialogStyle.Ok);
    }

    We should probably add a little border for the dialog surface.

    generic.xaml:

    <Style TargetType="local:MessageBoxControl">
    .....
    <Setter Property="BorderBrush" Value="Black"></Setter>
    <Setter Property="BorderThickness" Value="1"></Setter>
    <Setter Property="Template">
    <Setter.Value>
    <ControlTemplate TargetType="local:MessageBoxControl">
    <Grid x:Name="RootElement" Background="{TemplateBinding Background}">
    <Rectangle Stroke="{TemplateBinding BorderBrush}"
    StrokeThickness="{TemplateBinding BorderThickness}"></Rectangle>
    <Grid Margin="{TemplateBinding BorderThickness}">
    .....
    </Grid>
    </Grid>
    </ControlTemplate>
    </Setter.Value>
    </Setter>
    </Style>

    Implementing Window Dragging

    This is very simple now that our parent is a Canvas since we can manage the control's position.

    public MessageBoxControl(Popup owner) 
    : this() {
    this._owner = owner;
    this.MouseLeftButtonDown += new MouseButtonEventHandler(Surface_MouseLeftButtonDown);
    this.MouseLeftButtonUp += new MouseButtonEventHandler(Surface_MouseLeftButtonUp);
    this.MouseMove += new MouseEventHandler(Surface_MouseMove);
    }
     
    #region Dragging

    bool _hasNCHitTest = false;
    Point _initalNCHitTest = new Point();

    void Surface_MouseMove(object sender, MouseEventArgs e) {
    if (e.Handled == true)
    return;
    if (_hasNCHitTest) {
    Point hitTest;
    hitTest = e.GetPosition(this);

    Point delta = new Point(
    hitTest.X - _initalNCHitTest.X,
    hitTest.Y - _initalNCHitTest.Y);

    this.SetValue(Canvas.TopProperty, (double)this.GetValue(Canvas.TopProperty) + delta.Y);
    this.SetValue(Canvas.LeftProperty, (double)this.GetValue(Canvas.LeftProperty) + delta.X);

    e.Handled = true;
    }
    }

    void Surface_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
    if (e.Handled == true)
    return;
    if (_hasNCHitTest) {
    _hasNCHitTest = false;
    this.ReleaseMouseCapture();
    e.Handled = true;
    }
    }

    void Surface_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
    if (e.Handled == true)
    return;
    _hasNCHitTest = true;
    _initalNCHitTest = e.GetPosition(this);
    this.CaptureMouse();
    e.Handled = true;
    }

    #endregion

    That's all there is too it. Well kinda :). Remember the declaration for the MessageBox.Show?

    public static DialogResult Show(string title, string text, DialogStyle dialogStyle) {
    ....
    return DialogResult.None;
    }

    It assumes that the call to MessageBox.Show will be a blocking one, which is not! So how should we get the DialogResult back? We'll look at this problem in Part III.

    UPDATE: To answer James' questions below about the resizing of the background canvas. This can be accomplished by hooking into the Application.Host.Current.Content.Resized event. For example:

    public MessageBoxControl(Popup owner, DialogCloseDelegate callback)
        : this() {
        this._owner = owner;
        this._callback = callback;
        this.MouseLeftButtonDown += new MouseButtonEventHandler(Surface_MouseLeftButtonDown);
        this.MouseLeftButtonUp += new MouseButtonEventHandler(Surface_MouseLeftButtonUp);
        this.MouseMove += new MouseEventHandler(Surface_MouseMove);
        Application.Current.Host.Content.Resized += new EventHandler(Content_Resized);
    }
    
    void Content_Resized(object sender, EventArgs e) {
        if (_owner != null && _owner.Child != null) {
            Panel backgroundPanel = _owner.Child as Panel;
            if (backgroundPanel != null) {
                backgroundPanel.Width = Application.Current.Host.Content.ActualWidth;
                backgroundPanel.Height = Application.Current.Host.Content.ActualHeight;
            }
        }
    }

     

    Cheers

    Azret

  • Custom Silverlight Controls: Creating a reusable MessageBox dialog. (Part I)

    There is no support for modal dialogs in Silverlight 2.0 and there is no quick way to say MessageBox.Show("Hello World"). There is a Popup "primitive" however that can be used to create such things.

    For example

    Page.xaml:

    <Popup x:Name="myMessageBox">
    <Grid Width="400" Height="150">
    <Rectangle Stroke="Black" StrokeThickness="1"/>
    <Grid Margin="1">
    <Grid.RowDefinitions>
    <RowDefinition Height="Auto"></RowDefinition>
    <RowDefinition Height="*"></RowDefinition>
    <RowDefinition Height="Auto"></RowDefinition>
    </Grid.RowDefinitions>
    <Grid Grid.Row="0" x:Name="HeaderElement" Background="Navy">
    <TextBlock Text="Message" Foreground="White"></TextBlock>
    </Grid>
    <Grid Grid.Row="1" x:Name="ContentElement" VerticalAlignment="Stretch">
    <TextBlock Text="Hello World"
    VerticalAlignment="Center"
    HorizontalAlignment="Center"></TextBlock>
    </Grid>
    <Grid Grid.Row="2" x:Name="ButtonsPanelElement" Margin="2">
    <StackPanel Margin="4">
    <Button Content="OK" Width="75" Click="PopupClose"/>
    </StackPanel>
    </Grid>
    </Grid>
    </Grid>
    </Popup>

    Page.xaml.cs:

    private void ShowMessageBox(object sender, RoutedEventArgs e) {
    myMessageBox.HorizontalOffset = 100;
    myMessageBox.VerticalOffset = 100;
    myMessageBox.IsOpen = true;
    }

    private void PopupClose(object sender, RoutedEventArgs e) {
    myMessageBox.IsOpen = false;
    }
     

     

    We are still missing support for dragging, "modality" and the ability to customize and reuse this control. Implementing all of this inside the application itself quickly leads to unreadable and unmaintainable code. Fortunately,  it is easy enough to create a reusable library that will wrap all this plumbing. This is what we want:

    public enum DialogResult {
    None,
    Ok,
    Cancel,
    }

    public enum DialogStyle {
    Ok,
    OkAndCancel,
    }

    public static class MessageBox {
    public static DialogResult Show(string title, string text, DialogStyle dialogStyle) {
    // TODO:
    }
    }

    Part I: Creating a MessageBox content presenter

    In the newly created Silverlight Class Library (DevExpressTV.Forms.dll), here is our dummy MessageBoxControl.

    MessageBox.xaml.cs:

    public class MessageBoxControl : ContentControl {
    public MessageBoxControl() {
    DefaultStyleKey = typeof(MessageBoxControl);
    }
    }

    generic.xaml:

    <ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:DevExpressTV.Forms"
    xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows">
    <Style TargetType="local:MessageBoxControl">
    <Setter Property="Template">
    <Setter.Value>
    <ControlTemplate TargetType="local:MessageBoxControl">
    <Grid x:Name="RootElement" Background="{TemplateBinding Background}">
    <ContentPresenter
    VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
    HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
    Content="{TemplateBinding Content}"
    ContentTemplate="{TemplateBinding ContentTemplate}"/>
    </Grid>
    </ControlTemplate>
    </Setter.Value>
    </Setter>
    </Style>
    </ResourceDictionary>

    Adding Header Support

    MessageBox.xaml.cs::MessageBoxControl:

    public static readonly DependencyProperty HeaderContentProperty =
    DependencyProperty.Register("HeaderContent", typeof(object), typeof(MessageBoxControl), null);

    public object HeaderContent {
    get { return (object)GetValue(HeaderContentProperty); }
    set { SetValue(HeaderContentProperty, value); }
    }

    public static readonly DependencyProperty HeaderForegroundProperty =
    DependencyProperty.Register("HeaderForeground", typeof(Brush), typeof(MessageBoxControl), null);

    public Brush HeaderForeground {
    get { return (Brush)GetValue(HeaderForegroundProperty); }
    set { SetValue(HeaderForegroundProperty, value); }
    }

    public static readonly DependencyProperty HeaderBackgroundProperty =
    DependencyProperty.Register("HeaderBackground", typeof(Brush), typeof(MessageBoxControl), null);

    public Brush HeaderBackground {
    get { return (Brush)GetValue(HeaderBackgroundProperty); }
    set { SetValue(HeaderBackgroundProperty, value); }
    }

    generic.xaml:

    <Style TargetType="local:MessageBoxControl">
    <Setter Property="HeaderBackground" Value="Navy"></Setter>
    <Setter Property="HeaderForeground" Value="White"></Setter>
    <Setter Property="Template">
    <Setter.Value>
    <ControlTemplate TargetType="local:MessageBoxControl">
    <Grid x:Name="RootElement" Background="{TemplateBinding Background}">
    <Grid>
    <Grid.RowDefinitions>
    <RowDefinition Height="Auto"></RowDefinition>
    <RowDefinition Height="*"></RowDefinition>
    </Grid.RowDefinitions>
    <Grid
    Background="{TemplateBinding HeaderBackground}"
    x:Name="HeaderRootElement">
    <ContentPresenter
    Foreground="{TemplateBinding HeaderForeground}"
    Margin="2,0,0,0"
    x:Name="HeaderPresenterElement"
    VerticalContentAlignment="Center"
    HorizontalContentAlignment="Left"
    Content="{TemplateBinding HeaderContent}"/>
    </Grid>
    <ContentPresenter
    x:Name="ContentPresenterElement"
    Grid.Row="1"
    VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
    HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
    Content="{TemplateBinding Content}"
    ContentTemplate="{TemplateBinding ContentTemplate}"/>
    </Grid>
    </Grid>
    </ControlTemplate>
    </Setter.Value>
    </Setter>
    </Style>

    Page.xaml:

    <DevExTV:MessageBoxControl Background="AliceBlue" 
    HeaderContent="MessageBox" Width="300" Height="150" Content="Hello World">
    </DevExTV:MessageBoxControl>

     

    Creating Dialog Buttons

    public static readonly DependencyProperty DialogStyleProperty =
    DependencyProperty.Register("DialogStyle", typeof(DialogStyle), typeof(MessageBoxControl),
    new PropertyMetadata(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) {
    MessageBoxControl c = d as MessageBoxControl;
    if (c != null) {
    c.CreateDialogButtons();
    }
    }));

    public DialogStyle DialogStyle {
    get { return (DialogStyle)GetValue(DialogStyleProperty); }
    set { SetValue(DialogStyleProperty, value); }
    }

    The number of Dialog buttons that we want displayed changes depending on the DialogStyle property. So we will need to create them at runtime and place them inside some container panel. To save time, I will use the StackPanel with the Orientation set to "Horizontal" as the container for the dialog buttons.

    Updated Control Template in generic.xaml:

    <ControlTemplate TargetType="local:MessageBoxControl">
    <Grid x:Name="RootElement" Background="{TemplateBinding Background}">
    <Grid>
    <Grid.RowDefinitions>
    <RowDefinition Height="Auto"></RowDefinition>
    <RowDefinition Height="*"></RowDefinition>
    <RowDefinition Height="Auto"></RowDefinition>
    </Grid.RowDefinitions>
    <Grid
    Background="{TemplateBinding HeaderBackground}"
    x:Name="HeaderRootElement">
    <ContentPresenter
    Foreground="{TemplateBinding HeaderForeground}"
    Margin="2,0,0,0"
    x:Name="HeaderPresenterElement"
    VerticalContentAlignment="Center"
    HorizontalContentAlignment="Left"
    Content="{TemplateBinding HeaderContent}"/>
    </Grid>
    <ContentPresenter
    x:Name="ContentPresenterElement"
    Grid.Row="1"
    VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
    HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
    Content="{TemplateBinding Content}"
    ContentTemplate="{TemplateBinding ContentTemplate}"/>
    <Grid Grid.Row="2" Margin="2,2,2,8">
    <StackPanel x:Name="ButtonsPanel"
    Orientation="Horizontal"
    HorizontalAlignment="Center"/>
    </Grid>
    </Grid>
    </Grid>
    </ControlTemplate>

    CreateDialogButtons: For simplicity, I hard-coded the content for the OK and Cancel buttons.

    void CreateDialogButtons() {
    Panel panel = this.ButtonsPanel;
    if (panel != null) {
    panel.Children.Clear();
    switch (this.DialogStyle) {
    case DialogStyle.Ok:
    CreateButton("OK", this.ButtonTemplate, panel, DialogResult.Ok);
    break;
    case DialogStyle.OkAndCancel:
    CreateButton("OK", this.ButtonTemplate, panel, DialogResult.Ok);
    CreateButton("Cancel", this.ButtonTemplate, panel, DialogResult.Cancel);
    break;
    default:
    throw new ArgumentOutOfRangeException("DialogStyle");
    }
    }
    }

    We call the CreateDialogButtons from 2 places. First when the template is applied to the control:

    public override void OnApplyTemplate() {
    base.OnApplyTemplate();
    this._buttonsPanel = GetTemplateChild("ButtonsPanel") as Panel;
    this.CreateDialogButtons();
    }

    And second when any property that controls a dialog button changes. For example (DefaultStyle, ButtonTemplate). You might have noticed this handler in the DialogStylePoprerty above.

    delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) {
    MessageBoxControl c = d as MessageBoxControl;
    if (c != null) {
    c.CreateDialogButtons();
    }

    We will do the same for the ButtonTemplate property. We expose this property for cases when we want to customize how a dialog button looks.

    public static readonly DependencyProperty ButtonTemplateProperty =
    DependencyProperty.Register("ButtonTemplate", typeof(ControlTemplate),
    typeof(MessageBoxControl), new PropertyMetadata(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) {
    MessageBoxControl c = d as MessageBoxControl;
    if (c != null) {
    c.CreateDialogButtons();
    }
    }));

    public ControlTemplate ButtonTemplate {
    get { return (ControlTemplate)GetValue(ButtonTemplateProperty); }
    set { SetValue(ButtonTemplateProperty, value); }
    }

    The code that actually creates a button: (Simplified for clarity)

    public class DialogButton : Button {
    DialogResult _dialogResult;
    public DialogButton(DialogResult dialogResult) {
    this._dialogResult = dialogResult;
    }
    }

    void CreateButton(object content, ControlTemplate template, Panel owner,
    DialogResult dialogResult) {

    DialogButton button = new DialogButton(dialogResult);
    button.Content = content;
    button.Width = 75;
    button.Margin = new Thickness(2, 0, 0, 0);
    if (template != null) {
    button.Template = template;
    }
    button.Click += new RoutedEventHandler(DialogButtonClick);
    owner.Children.Add(button);
    }

    void DialogButtonClick(object sender, RoutedEventArgs e) {
    }

    So far so good.

    <DevExTV:MessageBoxControl Background="AliceBlue" 
    HeaderContent="MessageBox"
    Width="300" Height="150" Content="Hello World"
    DialogStyle="OkAndCancel">
    </DevExTV:MessageBoxControl>

    we can verify that our ButtonTemplate is working as well:

    <UserControl.Resources>
    <ControlTemplate x:Key="CustomButtonTemplate">
    <Grid Background="Red">
    <ContentControl
    FontWeight="Bold"
    Content="{TemplateBinding Content}"
    VerticalContentAlignment="Center"
    HorizontalContentAlignment="Center"/>
    </Grid>
    </ControlTemplate>
    </UserControl.Resources>
     
    <Grid x:Name="LayoutRoot" Background="White">
    <DevExTV:MessageBoxControl Background="AliceBlue"
    HeaderContent="MessageBox"
    Width="300" Height="150" Content="Hello World"
    DialogStyle="OkAndCancel"
    ButtonTemplate="{StaticResource CustomButtonTemplate}">
    </DevExTV:MessageBoxControl>
    </Grid>

     

    OK, now that we have the basics for the MessageBox content presenter implemented, we can start putting our MessageBox.Show together. (Part II)

    Cheers,

    Azret

     

    PS: If you are following along, drop me quick email about something cool that you would like me to cover or implement as part of this article.

  • Linking a Chart Control to your Pivot Grid. (Helpful Tip)

    I was working on something recently that required a lot of data analyses. I used the PivodGrid control as it perfectly represented my data source. And one of the reports I was building had the data grouped by Month and by Day, so I wanted to see it as a line chart as well. Quickly poking around I discovered that the PivotGridControl itself can be used as a DataSource as it implements both the IBindingList and the ITypedList interfaces. There are three members (PropertyDescriptors) that the PivotGridControl::ITypedList exposes to help with chart integration. It is easier to visualize them in terms of the coordinate system. (Line chart for example.)

    1: "Series": This would be the the series legend.

    2: "Arguments": This would be the X-axis.

    3: "Values": This would be the Y-axis.

    and this is how the list would look like if bound to a regular grid:

     

    Binding the chart itself is easy (this will use the Auto-Created Series but you can also bind individual series the same way):

    chartControl1.DataSource = pivotGridControl;
    chartControl1.SeriesDataMember = "Series";
    chartControl1.SeriesTemplate.ArgumentDataMember = "Arguments";
    chartControl1.SeriesTemplate.ValueDataMembers.AddRange(new string[] { "Values" });

    And that's it. Here is a couple of lines to quickly create your pivot table from the database in case you want to try it out.

    const string SQL_GroupByMonth = 
    "select count(*) as Count, month(MyTable.DateColumm) as Month, " +
    "day(MyTable.DateColumm) as Day from MyTable " +
    "group by month(MyTable.DateColumm), day(MyTable.DateColumm) " +
    "order by Month, Day";
    var db = new SqlConnection("<CONNECTION STRING>");
    db.Open();
    var da = new SqlDataAdapter(SQL_GroupByMonth, db);
    var ds = new DataSet();
    da.Fill(ds);
    pivotGridControl.DataSource = ds.Tables[0];

     

     

    Cheers,

    Azret

  • Silverlight : More than one default template in generic.xaml?

    I was working on something recently that required more than one default template. In other words the default template to be used by a control was determined by another property.

    For example, suppose you have a Control called Shape

    public enum ShapeType {
    Red,
    Black,
    }

    public class Shape : Control {
    public Shape() {
    this.DefaultStyleKey = typeof(Shape);
    }

    public ShapeType Type {
    get { return (ShapeType)GetValue(TypeProperty); }
    set { SetValue(TypeProperty, value); }
    }
    }

    with a default style:

    <Style TargetType="local:Shape">
    <Setter Property="Template">
    <Setter.Value>
    <ControlTemplate TargetType="local:Shape">
    <Rectangle Fill="Red"></Rectangle>
    </ControlTemplate>
    </Setter.Value>
    </Setter>
    </Style>

    and you want the template to change based on the ShapeType. This is simple to do just by looking up the template from the resource dictionary.

    First thing to do is to refactor the generic.xaml a little bit so that different control templates are available as resource items.

    <ControlTemplate TargetType="local:Shape" x:Key="Shape:Black">
    <Rectangle Fill="Black"></Rectangle>
    </ControlTemplate>

    <ControlTemplate TargetType="local:Shape" x:Key="Shape:Red">
    <Rectangle Fill="Red"></Rectangle>
    </ControlTemplate>

    <Style TargetType="local:Shape">
    <Setter Property="Template" Value="{StaticResource Shape:Red}"></Setter>
    </Style>

    The initial value for the Template is the same as the default value for the ShapeType enum.

    Then, when the value of the Type changes, we will look up the control template:

    public static readonly DependencyProperty TypeProperty =
    DependencyProperty.Register("Type", typeof(ShapeType), typeof(Shape), new PropertyMetadata(new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) {
    Shape s = d as Shape;
    if (s != null) {
    s.OnShapeTypeChanged(true);
    }

    })));

     

    private string GetTemplateName(ShapeType type) {
    switch (type) {
    case ShapeType.Red:
    return "Shape:Red";
    case ShapeType.Black:
    return "Shape:Black";
    default:
    return null;
    }
    }
    protected void OnShapeTypeChanged(bool applyTemplate) {
    ResourceDictionary genericXaml = ResourceManager.GetResourceDictionary(null);
    if (genericXaml == null) {
    this.Template = null;
    return;
    }
    string templateName = GetTemplateName(this.Type);
    if (string.IsNullOrEmpty(templateName)) {
    this.Template = null;
    return;
    }
    this.Template = genericXaml[templateName] as ControlTemplate;
    if (applyTemplate) {
    this.ApplyTemplate();
    }
    }

    An additional helper class ResourceManager:

    internal static class ResourceManager {
    private static Dictionary<string, ResourceDictionary> _resourceDictionaryCache =
    new Dictionary<string, ResourceDictionary>();

    /// <summary>
    /// GetResourceDictionary
    /// </summary>
    public static ResourceDictionary GetResourceDictionary(string name) {
    var resourceName = name;
    if (string.IsNullOrEmpty(resourceName)) {
    resourceName = "generic.xaml";
    }
    ResourceDictionary retVal = null;
    if (_resourceDictionaryCache.TryGetValue(resourceName, out retVal)) {
    return retVal;
    }
    System.Reflection.Assembly assembly = typeof(ResourceManager).Assembly;
    string baseName = string.Format("{0}.g",
    assembly.FullName.Split(new char[] { ',' })[0]);
    string fullName = string.Format("{0}.resources", baseName);
    foreach (string s in assembly.GetManifestResourceNames()) {
    if (s == fullName) {
    System.Resources.ResourceManager manager =
    new System.Resources.ResourceManager(baseName, assembly);
    using (System.IO.Stream stream = manager.GetStream(resourceName)) {
    using (System.IO.StreamReader reader = new System.IO.StreamReader(stream)) {
    retVal = System.Windows.Markup.XamlReader.Load(reader.ReadToEnd()) as ResourceDictionary;
    if (retVal != null) {
    _resourceDictionaryCache.Add(resourceName, retVal);
    return retVal;
    }
    }
    }
    break;
    }
    }
    return null;
    }
    }

    And that's it.

     

    Cheers,

    Azret

« Previous page   Next page »
LIVE CHAT

Chat is one of the many ways you can contact members of the DevExpress Team.
We are available Monday-Friday between 7:30am and 4:30pm Pacific Time.

If you need additional product information, write to us at info@devexpress.com or call us at +1 (818) 844-3383

FOLLOW US

DevExpress engineers feature-complete Presentation Controls, IDE Productivity Tools, Business Application Frameworks, and Reporting Systems for Visual Studio, along with high-performance HTML JS Mobile Frameworks for developers targeting iOS, Android and Windows Phone. Whether using WPF, Silverlight, ASP.NET, WinForms, HTML5 or Windows 8, DevExpress tools help you build and deliver your best in the shortest time possible.

Copyright © 1998-2014 Developer Express Inc.
All trademarks or registered trademarks are property of their respective owners