in

Utah .NET User Group

Home of Utah's professional .NET developers.

Joe's Blog

  • New Blog

    New blog url:  http://xamlcoder.com/blog

    After many months of ignoring my blog, I finally decided to try and get it fixed.  After the last Community Server upgrade something in the software was broken, and I was unable to use Windows Live Writer to blog.  The upgrade was such a headache I loathed even the thought of spending more time trying to figure out the issue.  This week I finally decided to break down and figure it out.  However it appears to be too little too late.  Being unable to find the required upgrades on their website, I contacted »telligentsystems tech support.  This is what I received:

    “Unfortunately, we no longer offer the free versions of our products, nor do we support them any longer. We will not be able to provide you the upgrades. Please contact our sales department if you wish to purchase a license.”

    So, welcome to WordPress!  I was pleasantly surprised with a simple and seamless install.  After browsing through some of the available themes, I finally decided to create my own.  I created this theme based on Bea Stollnitz’s wonderful looking blog.  My goal is to eventually move my posts over, however for now my old blog will still be up and running.  For now, lets create some new XAML posts!

  • Building a Databound WPF Menu Using a HierarchicalDataTemplate

    I had a colleague ask me how he could solve a databinding problem while building a WPF Menu.  The immediate answer wasn’t apparent to me, though it’s actually quite simple.  A while back Karl Shifflett wrote up an excellent example of doing something similar.  I take his example one step further by showing how you can easily bind  properties to the MenuItem instead of assigning one-time values.

    Download Source:  WPFDataBoundMenu.zip

    We’re going to use a simple Business Object which represents our MenuItem.  It has four properties: MenuText, IsEnabled, Command, and Children.

    public class MyBusinessObject : INotifyPropertyChanged
    {
        private bool _isEnabled;
        private string _menuText;
        private ICommand _command;
        private ObservableCollection<MyBusinessObject> _children;
     
        public event PropertyChangedEventHandler PropertyChanged;
     
        public MyBusinessObject()
        {
            Children = new ObservableCollection<MyBusinessObject>();
        }
     
        public string MenuText
        {
            get { return _menuText; }
            set
            {
                _menuText = value;
                RaisePropertyChanged("MenuText");
            }
        }
     
        public bool IsEnabled
        {
            get { return _isEnabled; }
            set
            {
                _isEnabled = value;
                RaisePropertyChanged("IsEnabled");
            }
        }
     
        public ICommand Command
        {
            get { return _command; }
            set
            {
                _command = value;
                RaisePropertyChanged("Command");
            }
        }
     
        public ObservableCollection<MyBusinessObject> Children
        {
            get { return _children; }
            set
            {
                _children = value;
            }
        }
     
        private void RaisePropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
     
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    We’ll build our window to include a Menu and  a Button.  When we click the button we want to toggle the IsEnabled property of one of our Business Objects.

    image 

    <Window
        x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="Window1"
        Height="400"
        Width="700">
     
        <Window.Resources>
     
            <HierarchicalDataTemplate
                DataType="{x:Type local:MyBusinessObject}"
                ItemsSource="{Binding Path=Children}">
     
                <ContentPresenter
                    Content="{Binding MenuText}"
                    Loaded="ContentPresenter_Loaded"
                    RecognizesAccessKey="True" />
     
            </HierarchicalDataTemplate>
     
        </Window.Resources>
     
        <Grid
            Margin="10">
     
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
     
            <Menu
                ItemsSource="{Binding}"/>
     
            <StackPanel
                Grid.Row="1">
                <Button
                    Content="Toggle Enable"
                    Click="Enable_Click"/>
            </StackPanel>
     
        </Grid>
    </Window>

    In our code-behind for the window we build a collection of MyBusinessObject’s.  We then assign that collection to the DataContext of our Window.  This allows the Menu control to bind its ItemsSource to the current value of the DataContext (ItemsSource="{Binding}").  Where we do the magic is in the ContenPresenter_Loaded event handler.  This is fired when the MenuItems are being built.  Inside the handler we get a reference to the ContenPresenter then walk up the tree to it’s Parent MenuItem.  UtilityMethods.FindParent accomplishes this task by using the VisualTreeHelper and recursively walking the tree.  We then can get a reference to our MyBusinessObject through the DataContext of the MenuItem.  This allows us to build bindings, in code, to bind to various properties.  It’s important to remember that we can do lots of the same things in code in which we can do in XAML.  XAML is just usually funner.  =]

    public partial class Window1 : Window
    {
        private ObservableCollection<MyBusinessObject> _options;
     
        public Window1()
        {
            InitializeComponent();
     
            _options = new ObservableCollection<MyBusinessObject>();
     
            // build simple menu structure
            MyBusinessObject menuTwo = new MyBusinessObject
            {
                MenuText = "Two",
            };
     
            menuTwo.Children.Add(
                new MyBusinessObject
                {
                    MenuText = "Child 1"
                }
            );
            menuTwo.Children.Add(
                new MyBusinessObject
                {
                    MenuText = "Child 2",
                    IsEnabled = false
                }
            );
     
            _options.Add(
                new MyBusinessObject
                {
                    MenuText = "One"
                }
            );
            _options.Add(menuTwo);
     
            DataContext = _options;
        }
     
        private void ContentPresenter_Loaded(object sender, RoutedEventArgs e)
        {
            // get the content presenter
            ContentPresenter contentPresenter =
                sender as ContentPresenter;
     
            // get reference to menu item
            MenuItem menuItem =
                UtilityMethods.FindParent<MenuItem>(contentPresenter);
     
            // get reference to business object
            MyBusinessObject myBusinessObject =
                menuItem.DataContext as MyBusinessObject;
     
            // create bindings
            Binding isEnabledBinding = new Binding("IsEnabled");
            isEnabledBinding.Mode = BindingMode.TwoWay;
            isEnabledBinding.Source = myBusinessObject;
            menuItem.SetBinding(MenuItem.IsEnabledProperty, isEnabledBinding);
     
            Binding commandBinding = new Binding("Command");
            commandBinding.Mode = BindingMode.TwoWay;
            commandBinding.Source = myBusinessObject;
            menuItem.SetBinding(MenuItem.CommandProperty, commandBinding);
        }
     
        private void Enable_Click(object sender, RoutedEventArgs e)
        {
            // toggle IsEnabled
            _options[1].Children[1].IsEnabled =
                !_options[1].Children[1].IsEnabled;
        }
    }

    And that’s it!  I hope that helps!

    Download Source:  WPFDataBoundMenu.zip

    Joe

    Posted Nov 25 2008, 06:49 PM by Joe's Blog
    Filed under: ,
  • UI Design Patterns Presentation Code + Slide Deck

    Thank you to everyone who came to my presentation on UI Design patterns!  It was great to see so many people interested in patterns for building user interfaces.

    One user group member recorded the presentation on his laptop's webcam; I should have that available for download this weekend.  The video is so-so, but the audio isn't too bad.  Thanks Walt!

    Links

    Download Code & Slides - http://xamlcoder.com/joe/downloads/UIDesignPatterns2008.zip

    Martin Fowler MVC
    Martin Fowler MVP
    ASP.NET MVP & Other Patterns
    Karl Shifflett MVVM

    Requirements

    • Visual Studio 2008 SP1
    • Silverlight Tools for Visual Studio
    • NUnit (included with code)
    • Rhino Mocks (included with code

    There's a small "gotcha" with the Silverlight sample.  The implemenation is slightly different than that of it's WPF counterpart.  Silverlight Dependency properties do not automatically fire change notifications when their values have changed.  Therefore if you want property change notifications your control (read DependencyObject which the Dependency Property is a member of) itself has to implement the INotifyPropertyChanged interface, or you can do what I've done here and set the DataContext of a container control.

  • Utah .NET User Group Presentation - UI Design Patterns

    Join me this week at the Utah .NET User Group where I’ll be giving a presentation on UI Design Patterns.

    UI Design Patterns

    In this session we’ll explore three UI design patterns: Model View Controller (MVC), Model View Presenter (MVP), and Model View ViewModel (MVVM).  We’ll prove that you can reuse the same logic to drive a WinForms, ASP.NET, WPF, or Silverlight "View".  We’ll also see how using these patterns allow the bulk of your UI to be testable using testing frameworks such as NUnit and Rhino Mocks.

    Date: Thursday, November 13th, 2008

    Time: 6:00 PM

    Place: Neumont University 3rd Floor (10701 South River Front Parkway, South Jordan)

    See you there!

  • Building composite Applications Using PRISM Presentation

    Thank you to everyone who attended my presentation on PRISM at the Utah Code Camp this past weekend.  Here’s a link to my sample code and slide deck.

    http://xamlcoder.com/joe/downloads/PRISM-UTCodeCamp2008.zip

    Note that you’ll need VS2008 SP1 with the Silverlight Tools for Visual Studio 2008 SP1 installed to run the Silverlight demo.

  • Composite Silverlight 2.0 Application Library Updated to RTW

    I’ve updated my demo of the Composite Silverlight Library I built to work with Silverlight RTW.  The P&P group has plans to ship the next version of the PRISM library (PRISM 2.0) that supports Silverlight, so I’ve started to use their bits instead of the ones that I built with this sample.  Bellow is a screenshot of the conversion.  As you can see, the default styles in Silverlight 2.0 have been changed, and the rendering is much better.

    Download:  CompositeSilverlightRTW.zip

    Composite Silverlight (PRISM) Region Quickstart

  • Building a Silverlight ComboBox Using Attached Behaviors

    I recently needed to use a ComboBox in an application I was writing.  Because there is no built-in ComboBox in Silverlight I decided to explore building one using attached behaviors.  If you’re not familiar with this design pattern, check out Nikhil’s posts.  My ComboBox behavior is loosely based on his AutoComplete behavior.  I also make use of Julian’s ButtonCommands class, which he describes in this post.

    Silverlight ComboBox Project Test Page - Mozilla Firefox

    I started with a simple behavior interface:

    /// <summary>
    /// Represents a contract for encapsulation of logic that can be added
    /// to a dependency object through a pattern of attachment.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface IBehavior<T> where T : DependencyObject
    {
        /// <summary>
        /// Gets the associated object.
        /// </summary>
        /// <value>The associated object.</value>
        T AssociatedObject { get; }
     
        /// <summary>
        /// Attaches to the specified associated object.
        /// </summary>
        /// <param name="associatedObject">The associated object.</param>
        void Attach(T associatedObject);
     
        /// <summary>
        /// Detaches from the associated object.
        /// </summary>
        void Detach();
    }

    Which I used to implement a ComboBoxBehavior class.  I chose to use FrameworkElement because I wanted to be able to attach this ComboBox to different controls.  Possibly a TextBlock, a TextBox, a Border … any number of controls.  We’ll come back to this later …

    public class ComboBoxBehavior : IBehavior<FrameworkElement>
    {
        ...
    }

    After creating the behavior I created a simple static class which had a single attached property of type ComboBoxBehavior.

    public static class Behaviors
    {
        public static readonly DependencyProperty ComboBoxProperty =
            DependencyProperty.RegisterAttached(
                "ComboBox",
                typeof(ComboBoxBehavior),
                typeof(Behaviors),
                new PropertyMetadata(
                    new PropertyChangedCallback(OnComboBoxChanged)
                )
            );
        ...
    }

    Attach() and Detach() are called in the property changed callback.

    private static void OnComboBoxChanged(
        FrameworkElement element,
        ComboBoxBehavior oldValue,
        ComboBoxBehavior newValue)
    {
        if (oldValue != null)
        {
            oldValue.Detach();
        }
     
        if (newValue != null)
        {
            newValue.Attach(element);
        }
    }

    You can then wire up the behavior in XAML … I’m using a Border control in this example.

    <Border
        xmlns:local="clr-namespace:SilverlightComboBox">
        <local:Behaviors.ComboBox>
            <local:ComboBoxBehavior
                Opened="SimpleComboBoxBehavior_Opened"
                ItemSelected="SimpleComboBoxBehavior_ItemSelected"
                ShowComboBoxCommand="{StaticResource ShowComboBoxSimple}">
            </local:ComboBoxBehavior>
        </local:Behaviors.ComboBox>
        <TextBlock
            x:Name="uxSimpleComboBox"
            Margin="4,0,4,0">
        </TextBlock>
    </Border>

    So onto the workhorse … the behavior itself.  The way I built this behavior to work is it fires an event, Opened,  when the DropDown is opened, in which you can set the ItemsSource and the SelectedIndex of the ItemsSource, as demonstrated in the following code:

    private void SimpleComboBoxBehavior_Opened(
        object sender, ComboBoxOpenedEventArgs e)
    {
        List<string> strings = new List<string>();
        strings.Add("A");
        strings.Add("B");
        strings.Add("C");
        strings.Add("D");
     
        e.ItemsSource = strings;
     
        var index = strings.IndexOf(uxSimpleComboBox.Text);
        e.SelectedIndex = index;
    }

    Once the user selects a value another event is fired, ItemSelected, which can also be handled:

    private void SimpleComboBoxBehavior_ItemSelected(
        object sender, ComboBoxItemSelectedEventArgs e)
    {
        if (e.SelectedItem != null)
        {
            uxSimpleComboBox.Text = e.SelectedItem.ToString();
        }
    }

    You may be wondering why I took this approach.  I decided to go down the eventing route because as I mentioned previously, I wanted to be able to attach this behavior to lots of different controls.  As such I felt that it should be up to the implementer to display the data as needed.

    I also decided to use commands to drive the opening and closing of the DropDown.  This allows the user to declaratively specify when the DropDown is opened or closed.  If you look at the XAML for the behavior I’m wiring up a ShowComboBoxSimple command to the ShowComboBoxCommand.

    ShowComboBoxCommand="{StaticResource ShowComboBoxSimple}"

    This is an ICommand of type MultiDelegateCommand.  MultiDelegateCommand is a command which can register multiple delegates to call when it is executed.

    I registered a MultiDelegateCommand instance in the Application Resources to be able to use it in the markup:

    public Page()
    {
        Application.Current.Resources.Add(
            "ShowComboBoxSimple",
            new MultiDelegateCommand()
        );
     
        ...
     
        InitializeComponent();
     
        ... 
    }

    Lastly, I wanted to show the DropDown anytime any portion of the control I’m attaching to is clicked.  This is accomplished by subscribing to the MouseLeftButtonUp event when the associated object is being attached:

    /// <summary>
    /// Attaches to the specified associated object.
    /// </summary>
    /// <param name="associatedObject">The associated object.</param>
    public void Attach(FrameworkElement associatedObject)
    {
        AssociatedObject = associatedObject;
        AssociatedObject.MouseLeftButtonUp += AssociatedObject_MouseLeftButtonUp;
        AssociatedObject.LostFocus += AssociatedObject_LostFocus;
    }
    private void AssociatedObject_MouseLeftButtonUp(
        object sender, MouseButtonEventArgs e)
    {
        if (ShowComboBoxCommand != null && !IsDropDownOpen)
        {
            ShowComboBoxCommand.Execute();
        }
    }

    And that’s basically it!  You may be wondering ... does this control support complex types?  You betcha! The source show’s a sample of working with complex types.  Here is shown a complex City type with a Name property.

    <DataTemplate x:Key="CityTemplate">
        <TextBlock
            Text="{Binding Name}"
            ToolTipService.ToolTip="{Binding Name}"/>
    </DataTemplate>
     
    ...
     
    <local:Behaviors.ComboBox>
        <local:ComboBoxBehavior
            Opened="ComplexComboBoxBehavior_Opened"
            ItemSelected="ComplexComboBoxBehavior_ItemSelected"
            ShowComboBoxCommand="{StaticResource ShowComboBoxComplex}">
            <local:ComboBoxBehavior.DropDownTemplate>
                <DataTemplate>
                    <ListBox ItemTemplate="{StaticResource CityTemplate}" />
                </DataTemplate>
            </local:ComboBoxBehavior.DropDownTemplate>
        </local:ComboBoxBehavior>
    </local:Behaviors.ComboBox>
    ...

     

    Download: SilverlightComboBox.zip

    Hope that helps!

    Joe

  • Visual Studio 2008 Service Pack 1 (SP1) Released

    Today Microsoft made Visual Studio 2008 Service Pack 1 (SP1) available for download.  I was able to participate in a case study done on SP1 with Microsoft, Misys, and Veracity Solutions specifically using ADO.NET Data Services and the Entity Framework.  You can read the full case study here, though here’s a small snippet:

    “For more than a decade, Misys Healthcare Systems and Veracity Solutions have partnered to develop innovative applications that meet the needs of healthcare providers while improving the quality of patient care. To help medical staff reduce manual, paper-based processes, Misys Healthcare Systems and Veracity Solutions collaborated to create FreeNatal, a Web-based application that provides prenatal care providers with an easy-to-use, secure interface for managing patients’ records. Using Microsoft® Visual Studio® 2008 SP1 and the Microsoft .NET Framework 3.5 SP1, eight members from the Misys-Veracity team created the application. By taking advantage of these powerful technologies, the team increased development speed by 60 percent, enabling accelerated market delivery and further strengthening their respective positions in the healthcare informatics industry.”

     FreeNatal “Face Sheet” view.

    FreeNatal “Face Sheet” view.

     FreeNatal application architecture diagram.

    FreeNatal application architecture diagram.
     
  • Composite Silverlight 2.0 Beta 2 Application Library

    Lately I’ve been working with Composite WPF and Silverlight applications at Veracity Solutions.  I spent a few hours over the weekend converting the Composite WPF Application Library to Silverlight.  Tonight I finished porting one of the QuickStarts to Silverlight.  Of course, just today the P&P team posted a AGCompositeApplicationLibrary spike.  Just my luck!  Regardless, here’s the UIComposition QuickStart running in Silverlight using my ported libraries:

    Region Quickstart - Windows Internet Explorer

    Some things to note:

    • The TabItem style isn’t using binding to get the header value.
    • Location tab doesn’t include the map data.
    • I added a TabControlRegionAdapter class that works with the Silverlight TabControl.

    Special thanks to Michael Sync for posting his Silverlight Unity port, it got me started.

    Download: CompositeSilverlight.zip

  • Building Custom Template-able WPF Controls

    Building custom controls in WPF can provide you with lots of flexibility.  It allows you to entirely separate the behavior of the control from the look of the control.  This is the premise behind most of what WPF offers.  In this post I will show you how you can build a simple control similar to the search control in Outlook 2007.

     Filter TextBox

    Add a new WPF Application project.

    New Project

    Then add a WPF User Control Library.

    Add New Project

    Delete the generated UserControl1.xaml that was given to you.

    Microsoft Visual Studio

    Add a new WPF Custom Control.

    Add New Item - FilterTextBox

    Your solution should now look like this:

    Solution Explorer

     

    The template gives you a FilterTextBox.cs and Generic.xaml file.

    public class FilterTextBox : Control
    {
        static FilterTextBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(FilterTextBox),
                new FrameworkPropertyMetadata(typeof(FilterTextBox)));
        }
    }

     

    The default Generic.xaml is the default look for your custom control, and is found in the Theme directory.  It is just an empty border for now:

    <Style TargetType="{x:Type local:FilterTextBox}">
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type local:FilterTextBox}">
            <Border Background="{TemplateBinding Background}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="{TemplateBinding BorderThickness}">
            </Border>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>

     

    Now lets start building the behavior of our control.  We'll start by adding a 'Text' dependency property.  This will be the filter text that the user types in.  Notice that I've created callbacks to be notified when the property has changed.  And as always, do NOT put any code within the getter and setter of the CLR property, because WPF bypasses this property at runtime and calls GetValue and SetValue directly.  However the CLR property is still needed to use the property in xaml.

    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text",
                                    typeof(String),
                                    typeof(FilterTextBox),
                                    new UIPropertyMetadata(null,
                                        new PropertyChangedCallback(OnTextChanged),
                                        new CoerceValueCallback(OnCoerceText))
                                   );
     
    private static object OnCoerceText(DependencyObject o, Object value)
    {
        FilterTextBox filterTextBox = o as FilterTextBox;
        if (filterTextBox != null)
            return filterTextBox.OnCoerceText((String)value);
        else
            return value;
    }
     
    private static void OnTextChanged(DependencyObject o,
                                      DependencyPropertyChangedEventArgs e)
    {
        FilterTextBox filterTextBox = o as FilterTextBox;
        if (filterTextBox != null)
            filterTextBox.OnTextChanged((String)e.OldValue, (String)e.NewValue);
    }
     
    protected virtual String OnCoerceText(String value)
    {
        return value;
    }
     
    protected virtual void OnTextChanged(String oldValue, String newValue)
    {
    }
     
    public String Text
    {
        // IMPORTANT: To maintain parity between setting a property in XAML
        // and procedural code, do not touch the getter and setter inside
        // this dependency property!
        get
        {
            return (String)GetValue(TextProperty);
        }
        set
        {
            SetValue(TextProperty, value);
        }
    }    

     

    We'll want users of the control to be notified when the text in our TextBox has changed, so lets create a 'TextChanged' event.

    public static readonly RoutedEvent TextChangedEvent =
        EventManager.RegisterRoutedEvent("TextChanged",
                                        RoutingStrategy.Bubble,
                                        typeof(RoutedEventHandler),
                                        typeof(FilterTextBox));
     
    public event RoutedEventHandler TextChanged
    {
        add { AddHandler(TextChangedEvent, value); }
        remove { RemoveHandler(TextChangedEvent, value); }
    }

     

    Now lets go back and modify our OnTextChanged method to raise our TextChanged event.

    protected virtual void OnTextChanged(String oldValue, String newValue)
    {
        // fire text changed event
        this.RaiseEvent(new RoutedEventArgs(FilterTextBox.TextChangedEvent, this));
    }

     

    With the base behavior mostly done, we can move on to creating a generic look for our control.  Lets add a DockPanel with a Button and a TextBox, the Button docked to the right.  Lets also bind the 'Text' property of the TextBox to the 'Text' property of our control.  We want the 'UpdateSourceTrigger' to be 'PropertyChanged' so that the 'TextChanged' event we created will be fired every time the user types something into the TextBox.  Notice that we don't want the TextBox to have a border because then we'd have two borders.

    <ControlTemplate TargetType="{x:Type local:FilterTextBox}">
      <Border
          Background="{TemplateBinding Background}"
          BorderBrush="{TemplateBinding BorderBrush}"
          BorderThickness="{TemplateBinding BorderThickness}"
          CornerRadius="3">
        <DockPanel
          LastChildFill="True"
          Margin="1">
          <Button
            x:Name="PART_ClearFilterButton"
            Content="X"
            Width="20"
            ToolTip="Clear Filter"
            DockPanel.Dock="Right" />
          <TextBox
            x:Name="PART_FilterTextBox"
            Text="{Binding Path=Text,
                          Mode=TwoWay,
                          UpdateSourceTrigger=PropertyChanged,
                          RelativeSource={RelativeSource TemplatedParent}}"
            BorderBrush="{x:Null}"
            BorderThickness="0"
            VerticalAlignment="Center" />
        </DockPanel>
      </Border>
    </ControlTemplate>

     

    Take special note of the names of the controls.  They both begin with 'PART_'.  This is the standard way in WPF to signify controls that need to be replaced if you decide to change the template of the control.  If someone templates your control, you need some way to identify that the types used for your parts need to be ones that your control can use.  You can do this by using the TemplatePart attribute and giving it the name and type of your control parts.

    [TemplatePart(Name = "PART_FilterTextBox", Type = typeof(TextBox))]
    [TemplatePart(Name = "PART_ClearFilterButton", Type = typeof(Button))]
    public class FilterTextBox : Control
    {
        ...
    }

     

    We only want the 'Clear Filter' button to be showing when there is text in the TextBox, so lets create a DataTrigger to accomplish that for us.

    <ControlTemplate TargetType="{x:Type local:FilterTextBox}">
      <Border>
        ...
      </Border>
      <ControlTemplate.Triggers>
        <DataTrigger Binding="{Binding Path=Text.Length, ElementName=PART_FilterTextBox}" Value="0">
          <Setter TargetName="PART_ClearFilterButton" Property="Visibility" Value="Collapsed" />
        </DataTrigger>
      </ControlTemplate.Triggers>
    </ControlTemplate>

     

    Now we want to be able to handle when a user clicks on the 'Clear Filter' button.  You do this by overriding the OnApplyTemplate method.  You can get a reference to your controls by calling GetTemplateChild and passing the name of the control.  When the user clicks the 'Clear Filter' button we just want to remove any text in the TextBox.  We'll use the same strategy to get a reference to the TextBox control.

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
     
        Button clearFilterButton = base.GetTemplateChild("PART_ClearFilterButton") as Button;
        if (clearFilterButton != null)
        {
            clearFilterButton.Click += new RoutedEventHandler(ClearFilterButton_Click);
        }
    }
     
    private void ClearFilterButton_Click(Object sender, RoutedEventArgs e)
    {
        TextBox textBox = base.GetTemplateChild("PART_FilterTextBox") as TextBox;
     
        if (textBox != null)
        {
            textBox.Text = String.Empty;
        }
    }

     

    In the WPF Application project add a reference to the custom control project.

    Add Reference

    Now you can create a namespace for the controls project, labeled as controls here, and add your control to the form.

    <Window
        x:Class="FilterTextBoxDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:controls="clr-namespace:FilterTextBox;assembly=FilterTextBox"
        Title="FilterTextBox Demo"
        Height="300"
        Width="300">
      <StackPanel>
        <controls:FilterTextBox />
      </StackPanel>
    </Window>

     

    And we have a working control!

    Filter TextBox Demo 

    To make sure that the control still works when templated, lets go ahead and make a simple template for it.

    <Window ...>
      <Window.Resources>
        <Style x:Key="LOCAL_MyStyle" TargetType="{x:Type controls:FilterTextBox}">
          <Setter Property="BorderBrush" Value="CornFlowerBlue" />
          <Setter Property="BorderThickness" Value="2" />
          <Setter Property="Template">
            <Setter.Value>
              <ControlTemplate TargetType="{x:Type controls:FilterTextBox}">
                <Border
                  Background="{TemplateBinding Background}"
                  BorderBrush="{TemplateBinding BorderBrush}"
                  BorderThickness="{TemplateBinding BorderThickness}"
                  CornerRadius="10">
                  <DockPanel
                    LastChildFill="True"
                    Margin="5">
                    <Button
                      x:Name="PART_ClearFilterButton"
                      Content="--"
                      Width="30"
                      ToolTip="Clear Filter"
                      DockPanel.Dock="Left" />
                    <TextBox
                      x:Name="PART_FilterTextBox"
                      Text="{Binding Path=Text,
                              Mode=TwoWay,
                              UpdateSourceTrigger=PropertyChanged,
                              RelativeSource={RelativeSource TemplatedParent}}"
                      BorderBrush="{x:Null}"
                      BorderThickness="0"
                      VerticalAlignment="Center" />
                  </DockPanel>
                </Border>
                <ControlTemplate.Triggers>
                  <DataTrigger Binding="{Binding Path=Text.Length, ElementName=PART_FilterTextBox}" Value="0">
                    <Setter TargetName="PART_ClearFilterButton" Property="Visibility" Value="Collapsed" />
                  </DataTrigger>
                </ControlTemplate.Triggers>
              </ControlTemplate>
            </Setter.Value>
          </Setter>
        </Style>
      </Window.Resources>
      <StackPanel>
        <controls:FilterTextBox
          BorderBrush="#ACBFE4"
          Margin="10"
          TextChanged="FilterTextBox_TextChanged"/>
        <controls:FilterTextBox
          Style="{StaticResource LOCAL_MyStyle}"
          Margin="10"
          TextChanged="FilterTextBox_TextChanged"/>
      </StackPanel>
    </Window>

     

    Hope that helps!

    Joe

    Posted Dec 13 2007, 09:08 PM by Joe's Blog
    Filed under:
  • WPF Interoperability with Windows Forms and Office 2007

    Keith encountered some interesting behavior while trying to build a Custom Task Pane hosting a WPF control in VSTO to use in Excel.  (See my post on "Using WPF With VSTO & Office 2007").

    "I click the search textbox and it appears to have focus... but, when I start typing, text goes into the active cell and not the textbox..."

    Throwing together a simple control I experienced the same behavior:

    Test Addin - Input Focus Non-Activated

    Notice that the second TextBox contains the cursor, however the Task Pane "Test Addin" is not highlighted as if it's currently activated, which looks like this:

    Test Addin - Input Focus Activated

    So what's going on here?  Essentially it appears that the ElementHost is never getting a notification from the WPF message loop that a child control has taken input focus.  Excel is also "greedy" on handling keyboard input if a Task Pane or other control does not have input focus.  Reading this "Windows Forms and WPF Interoperability Input Architecture" MSDN article, it states:

    In Windows Forms, keyboard messages are routed to the window handle of the control that has focus. In the ElementHost control, these messages are routed to the hosted element. To accomplish this, the ElementHost control provides an HwndSource instance. If the ElementHost control has focus, the HwndSource instance routes most keyboard input so that it can be processed by the WPF InputManager class.

    So lets give the ElementHost focus!  Using the new Routed Event architecture we can have our ElementHost be notified anytime any WPF element that inherits from UIElement has been clicked.  I chose to use a Tunneling strategy to handle the MouseDown event.  The third argument "true" of the AddHandler method says that we want to be notified of ALL MouseDown events, even if another control handles the event and prevents it from continuing on.  You can read about Routed Events (Tunneling & Bubbling) in Adam Nathan's WPF Unleashed book, chapter 3.  (Freely available to download from Time Sneath's blog.)

    using Forms = System.Windows.Forms;

     

    private ElementHost m_ElementHost;

    private CustomTaskPane m_Pane;

    private Forms.UserControl m_UserControl;

     

    private void ThisAddIn_Startup(Object sender, System.EventArgs e)

    {

        m_ElementHost = new ElementHost();

        m_ElementHost.Child = new UserControl2();

        m_ElementHost.Child.AddHandler(

            UIElement.PreviewMouseDownEvent,

            new RoutedEventHandler(PreviewMouseDown_Event),

            true

        );

        m_ElementHost.Dock = Forms.DockStyle.Fill;

        m_UserControl = new Forms.UserControl();

        m_UserControl.Controls.Add(m_ElementHost);

        m_Pane = this.CustomTaskPanes.Add(m_UserControl, "Test Addin");

        m_Pane.Visible = true;

    }

     

    private void PreviewMouseDown_Event(Object sender, RoutedEventArgs e)

    {

        m_ElementHost.Focus();

    }

    Figure 3.9 (taken from Adam's book) shows the element hierarchy.

    WPF Element Hierarchy 

    Using this same strategy you could handle only giving the ElementHost, and therefore the Task Pane, focus when say just a TextBox is clicked or got focus, or a Button clicked or got focus.  However I thought using UIElement was a bit more succinct.  Hope that helps!

    Posted Sep 03 2007, 04:15 PM by Joe's Blog
    Filed under: , ,
  • Screencast: Building Interoperable Services using WSIT and WCF 3.0

    In this screencast I demonstrate how to build a secure, interoperable service using WSIT (Web Services Interoperability Technologies) and WCF 3.0 (Windows Communication Foundation).  This screencast uses certificates to secure a business to business scenario.  Click the image below to watch the screencast online.  You can double-click the player for full-screen mode.  The player is using Silverlight 1.0.

    screencast_Thumb

    Download directly:  http://xamlcoder.com/demos/net30/wsitcertificates/screencast.wmv

    This is the first screencast I've done, so let me know what you think.  If you'd like to see screencasts on other topics (relating to .NET 3.0 technologies) post your ideas!

    Demos Download:  http://xamlcoder.com/joe/downloads/WSITDemos.zip

    Links:

    NetBeans IDE:  http://netbeans.org

    WSIT Dev Site:  https://wsit.dev.java.net/

    Glassfish Dev Site:  https://glassfish.dev.java.net/downloads/v2-b50g.html

    Java One Lab: Make Java and .NET 3.0 interoperability work with WSIT

    Ant Script for Certificates:  CopyV3 script

    .NET 3.0 Visual Studio Extensions:  http://www.microsoft.com/downloads/details.aspx?familyid=f54f5537-cc86-4bf5-ae44-f5a1e805680d&displaylang=en

    This screencast was recorded using the free desktop recording software, CamStudio, and encoded using Microsoft Expression Media Encoder.

    P.S.  I know I refer to WSIT as "Web Services Interoperability Toolkit" instead of "Technologies" - sorry!

  • Binding to a DataSet in XAML

    At first glance binding a control (such as a ListBox) to a DataSet in XAML may not be apparent.  However all you need to remember is that the column names in a DataSet work very similar to the property names on plain CLR objects.  The trick is where you assign the DataSet to the listbox.  You can't use the ItemsSource property on a ListBox directly, because it expects an object of IEnumerable.  What you want is the DataContext.  This tells the control "Hey, I'm going to be working with this data and databinding with it."  The code below creates a simple DataSet with one table that has two columns: Name and Age, then assigns the DataSet to the DataContext of the ListBox control.

    public partial class Window1 : System.Windows.Window

    {

        public Window1()

        {

            InitializeComponent();

     

            this.Loaded += Window1_Loaded;

        }

     

        private void Window1_Loaded(Object sender, RoutedEventArgs e)

        {

            this.listBox.DataContext = GetData();

        }

     

        private DataSet GetData()

        {

            DataSet data = new DataSet("MyDataSet");

            DataTable table = data.Tables.Add("MyTable");

            table.Columns.Add("Name", typeof(String));

            table.Columns.Add("Age", typeof(Int32));

     

            for (int i = 0; i < 10; i++)

            {

                DataRow row = table.NewRow();

                row["Name"] = "Name " + (i + 1).ToString();

                row["Age"] = i * 10;

                table.Rows.Add(row);

            }

     

            return data;

        }

    }

    To be able to see your data in the ListBox you need to set a few properties in the XAML markup.  These are the ItemsSource and the ItemTemplate.  The Binding refers to the data that is currently associated to the DataContext.  So for us to bind to the data in the first table, the Path Tables[0]  is used.  Remember that using {Binding Tables[0]} and {Binding Path=Tables[0]} are synonymous.

    <Window x:Class="WindowsApplication1.Window1"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        Title="WindowsApplication1" Height="300" Width="300"

        >

     

        <Window.Resources>

     

          <DataTemplate x:Key="tableTemplate">

            <StackPanel Margin="3">

              <DockPanel LastChildFill="True">

                <TextBlock Text="Name:"

                          DockPanel.Dock="Left"

                          Margin="0,0,5,0"

                          />

                <TextBlock Text="{Binding Name}" />

              </DockPanel>

              <DockPanel LastChildFill="True">

                <TextBlock Text="Age:"

                          DockPanel.Dock="Left"

                          Margin="0,0,5,0"

                          />

                <TextBlock Text="{Binding Age}" />

              </DockPanel>

            </StackPanel>

          </DataTemplate>

     

        </Window.Resources>

     

        <Grid>

          <ListBox x:Name="listBox"

                  ItemsSource="{Binding Tables[0]}"

                  ItemTemplate="{StaticResource tableTemplate}"

                  >

          </ListBox>

        </Grid>

     

    </Window>

    If we were to change the code in the load event to bind directly against the table:

    private void Window1_Loaded(Object sender, RoutedEventArgs e)

    {

        this.listBox.DataContext = GetData().Tables[0];

    }

    The markup wound just change to ItemsSource="{Binding}" because the current DataContext has the data you are looking for.  If you only need to bind to one property on a DataSet, you can remove the ItemTemplate and use DisplayMemberPath instead.  Notice that the DataTemplate used for the DataSet looks exactly the same as if we were databinding to a plain CLR object.

    When you're all finished it looks like this:

    Binding to a DataSet in XAML - WindowsApplication1

    Hope that helps!

    Posted Aug 23 2007, 01:11 AM by Joe's Blog
    Filed under:
  • Using WPF With VSTO & Office 2007

    Hosting Windows Forms controls in Office 2007 Custom Task Pane's is pretty simple, as shown in this MSDN article.  I created a simple addin to display a list of image files in a given folder so that the user can double-click on the file name (in a ListBox) and insert the picture into the document.  With little effort it was simple to instead implement a WPF UserControl to spruce up the addin.  You can download the code here.

    Document1 - Microsoft Word

     

    You can host WPF Elements using the ElementHost control found in the WindowsFormsIntegration assemly that ships with the .NET 3.0 runtime.  The CustomTaskPane requires a Windows Forms UserControl, so I'm just adding the ElementHost control to a plain Windows Forms UserControl and then passing that UserControl to be created with the CustomTaskPane.

    internal void AddTaskPane()

    {

        ElementHost host = new ElementHost();

        browser = new ImageBrowserControl();

        host.Child = browser;

        host.Dock = DockStyle.Fill;

     

        browser.PictureSelected += ImageBrowserControl_PictureSelected;

     

        UserControl userControl = new UserControl();

        userControl.Controls.Add(host);

     

        pane = this.CustomTaskPanes.Add(userControl, "Image Browser");

     

        pane.Visible = true;

    }

     

    Inserting images into a Word document is just as simple, as shown in the following code snippet:

    private void ImageBrowserControl_PictureSelected(Object sender, EventArgs<Picture> e)

    {

        Object missing = System.Reflection.Missing.Value;

     

        Globals.ThisAddIn.Application.Selection.InlineShapes.AddPicture(

            e.Item.Filename, ref missing, ref missing, ref missing);

    }

     

    The EventArgs<T> class used in the previous handler is a nice trick to use in conjunction with EventHandler<T>.  A tip from Jean-Paul S. Boodhoo's Blog.

    public class EventArgs<T> : EventArgs

    {

        private T m_Item;

     

        public EventArgs(T item)

        {

            m_Item = item;

        }

     

        public T Item

        {

            get

            {

                return m_Item;

            }

        }

    }

     

    public event EventHandler<EventArgs<Picture>> PictureSelected;

     

    To create  a Ribbon tab you define a set of XML.  The onAction on the button is an event that you can handle in your codebehind file.

    <customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="OnLoad">

      <ribbon>

        <tabs>

          <tab id="VSTOAddIns" label="Addins" >

            <group id="Group1" label="Helpers" visible ="1">

              <button id="ImageBrowser" label="Insert Image"

              onAction="ImageButtonClick"

              imageMso="ContentControlDate"/>

            </group>

          </tab>

        </tabs>

      </ribbon>

    </customUI>

     

    Handler for toggling showing / hiding the task pane:

    public void ImageButtonClick(Office.IRibbonControl control)

    {

        if (!m_ImagePaneExists)

        {

            Globals.ThisAddIn.AddTaskPane();

        }

        else

        {

            Globals.ThisAddIn.RemoveTaskPane();

        }

     

        m_ImagePaneExists = !m_ImagePaneExists;

    }

     

     The WPF UserControl styles a simple Picture class, shown below:

    public class Picture

    {

        private FileInfo m_FileInfo;

     

        public Picture(String filename)

        {

            m_FileInfo = new FileInfo(filename);

        }

     

        public String Filename

        {

            get { return m_FileInfo.FullName; }

            set { m_FileInfo = new FileInfo(value); }

        }

     

        public String Name

        {

            get { return m_FileInfo.Name; }

        }

    }

     

     DataTemplate for the Picture class:

    <DataTemplate DataType="{x:Type local:Picture}">

      <StackPanel MinHeight="0px" MinWidth="0px" MaxHeight="150px" MaxWidth="150px" >

        <Image Stretch="UniformToFill" Source="{Binding Filename}" />

        <TextBlock Text="{Binding Name}"

                  FontWeight="Bold"

                  HorizontalAlignment="Center"

                  />

      </StackPanel>

    </DataTemplate>

     

    The ListBox style is taken from my previous blog post on styling ListBoxItem's.

    <Style TargetType="ListBoxItem">

      <Setter Property="Template">

        <Setter.Value>

          <ControlTemplate TargetType="ListBoxItem">

            <Border x:Name="ItemBorder"

                    BorderBrush="Black"

                    Background="LightGray"

                    BorderThickness="2"

                    CornerRadius="4"

                    Margin="3"

                    >

              <ContentPresenter Margin="2" />

            </Border>

            <ControlTemplate.Triggers>

              <Trigger Property="IsSelected" Value="True">

                <Setter TargetName="ItemBorder"

                        Property="BorderBrush"

                        Value="Red"

                        />

              </Trigger>

              <Trigger Property="IsMouseOver" Value="True">

                <Setter TargetName="ItemBorder"

                        Property="BorderBrush"

                        Value="Blue"

                        />

              </Trigger>

              <MultiTrigger>

                <MultiTrigger.Conditions>

                  <Condition Property="IsMouseOver" Value="False" />

                  <Condition Property="IsSelected" Value="False" />

                </MultiTrigger.Conditions>

                <Setter TargetName="ItemBorder"

                        Property="Opacity"

                        Value="0.50"

                        />

              </MultiTrigger>

            </ControlTemplate.Triggers>

          </ControlTemplate>

        </Setter.Value>

      </Setter>

    </Style>

     

    A couple things to remember when building Word Addins is that all Assembly's that you use need to be signed and granted full-trust by the .NET runtime for Word to load them.  Other than that creating Word Addins is pretty simple!

    Posted Jul 17 2007, 09:31 PM by Joe's Blog
    Filed under: ,
  • Querying Excel Files Using ADO.NET

    Recently I needed to query an excel file and import that data into a database.  I could not immediately recall how to accomplish this, so with a bit of searching I found the connection settings that I needed.  Bellow is a code snippet that can be used to load all of the data in a workbook into a GridView on an ASP.NET page.  Note the use of the "Extended Properties='Excel 8.0;'" in the connection template as well as Sheet1$ in the SQL command to specify the name of the workbook in the worksheet.

    using System;

    using System.Data;

    using System.Data.OleDb;

    using System.Web.UI;

     

    public partial class _Default : Page

    {

        protected void Page_Load(Object sender, EventArgs e)

        {

            GridView1.DataSource = GetProductData();

            GridView1.DataBind();

        }

     

        private DataSet GetProductData()

        {

            String connectionTemplate = @"Provider=Microsoft.Jet.OLEDB.4.0;" +

                "Data Source={0}; Extended Properties='Excel 8.0;'";

     

            using (OleDbConnection connection = new OleDbConnection(

                    String.Format(connectionTemplate,

                                  Server.MapPath("~/App_Data/Products.xls"))

                  ))

            {

                OleDbCommand command = new OleDbCommand(

                    "Select * FROM [Sheet1$]", connection

                );

     

                OleDbDataAdapter adapter = new OleDbDataAdapter(command);

     

                DataSet dataSet = new DataSet();

     

                adapter.Fill(dataSet);

     

                return dataSet;

            }

        }

    }

More Posts Next page »
Copyright © 2000-2007, Utah .NET User Group
Powered by Community Server (Commercial Edition), by Telligent Systems