Silverlight Tags Control Using RichTextBox, Popup and ListBox.


 

Download Sample

 

 

Before couple of days I created a an editable tagging control with autocomplete list.
I decided to create one “some how” Smile similar to Live mail application “To” field.

to

That’s exactly what I need; RichTextBox, Popup Control, and a ListBox inside that popup, and SL Cinch MVVM Framework.

And one more thing InlineUIContainer that contains my Tag Item control.

InlineUIContainer object used to wrap FrameworkElement controls inside RichTextBox; so the only way to add (border, StackPanel, or even buttons) is by using this object.

Note:

Buttons will be disabled if RichTextBox ReadOnly property equal false.
So if you want to add delete button to your Tag Item; I think that you should to make your RichTextBox works in modes (Editable, ReadOnly).
Editable: to add inline tag items.
RedOnly: to click on delete buttons for each tag Item.

I did not like this kind of user experience. So I just delete delete button from tag item. Make things simpler that better Smile. You can just delete it using backspace or by highlighting some item then delete “As you saw in Video”.

 

When you download the sample, you will find a Silverlight library project called bunjeeb.SL.Controls

Three DLLs added to this project:

  • Microsoft.Expression.Interactions
  • System.Windows.Interactivity
  • Cinch.SL

You can find these three DLLs in dependencies folder with the sample.

As you see below TagTextBox is very simple:

<Grid x:Name="LayoutRoot">

<Popup x:Name="PopupTags" IsOpen="False">
<Grid>
<ListBox x:Name="PopupTagsList" Height="Auto" 
    MinWidth="200" MaxHeight="250"
    Background="White" 
    BorderBrush="Black" 
    KeyDown="PopupListBox_KeyDown"
    ItemContainerStyle="{StaticResource 
                          ListBoxItemContainerStyle}" 
    Padding="1,1,1,2"/>
</Grid>
</Popup>

<RichTextBox x:Name="rtb"
   AcceptsReturn="False"
   BorderBrush="{x:Null}" 
   Background="{x:Null}" BorderThickness="1,1,0,0">
   <i:Interaction.Triggers>
     <i:EventTrigger EventName="KeyUp">
      <Cinch:EventToCommandTrigger 
         Command="{Binding RichTextBoxKeyUp, 
                  ElementName=TagsTxtBox}"/>
     </i:EventTrigger>
     <i:EventTrigger EventName="KeyDown">
      <Cinch:EventToCommandTrigger 
         Command="{Binding RichTextBoxKeyDown,
                  ElementName=TagsTxtBox}"/>
     </i:EventTrigger>
     <i:EventTrigger EventName="ContentChanged">
      <Cinch:EventToCommandTrigger 
         Command="{Binding RichTextBoxContentChanged, 
                  ElementName=TagsTxtBox}"/>
     </i:EventTrigger>
     <i:EventTrigger EventName="LostFocus">
        <Cinch:EventToCommandTrigger 
         Command="{Binding RichTextBoxLostFocus, 
                  ElementName=TagsTxtBox}"/>
     </i:EventTrigger>
   </i:Interaction.Triggers>
</RichTextBox>

</Grid>

 

As you see here; I used EventToCommandTrigger to invoke these events using Commands. And that’s gonna help MVVMers to create there own ViewModel for that control. (If they want)

I’m sure that Sacha Barber (This one who creates Cinch Framework) used WeakReferences. and that gonna help GC to do his work. Smile Or simply you just use the delegate commands, or relay commands instead in PRISM. For me I prefer to use Cinch framework; I think it’s a preference.

TagsTextBox Control contains three properties:

  • Tags DependencyProperty: To display tags in RichTextBox.
  • AllTags DependencyProperty: Which contains all tags to display it on autocomplete popup.
  • InlineUITags CLR read only property: Which contains the Tag Items that you can find it in RichTextBox. 

When user starts typing FilterPopupList will be executed. The Main functionality of this Method is to:

  • Iterating into all InlineUIContainers to get tags texts.
  • Tags Popup will display = all tags except existing tags in RichTextBox.
private void FilterPopupList(string changedText)
{
   if (changedText == null)
   {
      PopupTagsList.ItemsSource = null;
      return;
   }

   //Get Tags from RichTextBox
   List<string> newTags = new List<string>();
   foreach (InlineUIContainer inlineUiContainer 
                in this.InlineUITags)
   {
     TextBlock tagNameTxtBlock = (TextBlock)((StackPanel)
      (((Border)inlineUiContainer.Child).Child)).Children[0];
            newTags.Add(tagNameTxtBlock.Text);
   }

   IEnumerable<string> tags = this.AllTags
                .Where(x => x.ToLower()
                         .Contains(changedText.ToLower()))
                                      .Except(newTags);

   PopupTagsList.ItemsSource = null;
   PopupTagsList.ItemsSource = tags;
}

 

When user commit tag insertion (by clicking enter or by choosing one of the items in the list); Then the source should be updated.

UpdateTagsPropertyAndBindingSource will do this work. Let me explain the logic
this method in three points.
  • Reset popup state if you want using isResetPopupState parameter.
  • Collecting the new added Tags.
  • Some Tags were deleted; so we should tell the source about it.
  • And Some tags were added; and we should tell the source about it also.
  • Update source using binding.UpdateSource()
  • And Finally, Raise TagsChanged event which contains Tags Added & Tags Deleted.

  • private void UpdateTagsPropertyAndBindingSource(
              bool isResetPopupState)
    {
          if (isResetPopupState)
             ResetPopupState();
    
          IEnumerable<string> newTags = GetNewItemsAdded();
    
          // If some item found in Tags Collection And 
          // not in RichTextBox; So we should delete it.
          IEnumerable<string> mustBeDeleted = 
                               this.Tags.Except(newTags);
          List<string> mustBeDeletedList = null;
    
          //Tags must be deleted from the source
          int mustBeDeletedCount = mustBeDeleted.Count();
          int lastRemoveIndex = mustBeDeletedCount - 1;
          if (mustBeDeletedCount > 0)
          {
             mustBeDeletedList = mustBeDeleted.ToList();
             for (int i = lastRemoveIndex; i >= 0; i--)
               this.Tags.Remove(mustBeDeletedList[i]);
          }
    
           //Tags must be added to the source
           IEnumerable<string> mustBeAdded = 
                                 newTags.Except(this.Tags);
           IEnumerable<string> mustBeAddedList = null;
           int mustBeAddedCount = mustBeAdded.Count();
           if (mustBeAddedCount > 0)
           {
             mustBeAddedList = mustBeAdded.ToList();
             foreach (string mustAddItem in mustBeAdded)
               this.Tags.Add(mustAddItem);
            }
    
            if (mustBeDeletedCount > 0 || mustBeAddedCount > 0)
            {
             BindingExpression binding = 
               this.GetBindingExpression(TagsTextBox.TagsProperty);
               if (null != binding) binding.UpdateSource();
    
               if (this.TagsChanged != null)
               {
                 TagsChanged(binding.DataItem, 
                   new TagsChangedArgs()
                 {
                    TagOwner = binding.DataItem,
                    TagsAdded = mustBeAddedList,
                    TagsDeleted = mustBeDeletedList
                 });
              }
         }
     }

Ok, One more thing to know, What If you have multiple instances of this control. And as you see in the video; that you can add entire new tag which is not listed in the auto complete popup list.

So how could tell other controls that All Tags List changed?

If sure that you folks have many many solutions in binding, Observable collection has the ability to notify the UI when item added. yeah you are right.

I decided to use the Mediator in Cinch framework. Its really amazing (pub sub mechanism) which is like Event Aggregator in PRISM.

So I just publish new All Tags List.

I just passed the Id to refocus RichTextBox.

[MediatorMessageSink(bunjeeb.SL.Common.MediatorMessages.
            TagsChangedMessage.TagsChanged)]
        public void CandidateTagsChangedMessage(
            Tuple<int, IEnumerable<string>, Type> tuple)
    {
       this.AllTags = tuple.Item2;

       PropertyInfo pinfo = tuple.Item3.GetProperty("Id");
       int currentId = (int)pinfo.GetValue(this.DataContext,
                                                null);
       int updatedId = tuple.Item1;

       if (updatedId == currentId)
           this.rtb.Focus();
  }

 

So the above code for the subscriber. And the publisher is our main view model.

public ICommand TagsChangedCommand { get; set; }
private void OnTagsChangedCommand(EventToCommandArgs args)
{
    TagsChangedArgs e = args.EventArgs as TagsChangedArgs;

    // TODO: Remove, add tags, Submit Changes to RIA Service
    // e.TagsAdded
    // e.TagsDeleted

    bunjeeb.SL.Common.MediatorMessages.TagsChangedMessage
       .Send(new Tuple<object>(e.TagOwner));
}

 

This command is binded with our TagsTextBox.TagsChanged Event

<bunjeebControls:TagsTextBox x:Name="MyTagsTextBox2"
   AllTags="{Binding AllTags}" 
   Tags="{Binding SomeTags2, Mode=TwoWay}" >

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="TagsChanged">
            <Cinch:EventToCommandTrigger 
        Command="{Binding TagsChangedCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>

</bunjeebControls:TagsTextBox>

 

Sorry for not completing the 2nd article of pivot view control. I found that its more important to talk about Tagging Control. Since I did not any Silverlight Tagging Control.

That’s it. Wishing best coding in your life Smile

Download Sample

Silverlight Pivot View Control – Part 1


 

Download Sample

 

Just before one month from now, I worked with a new company; I really loved my new Job. New experience taken from Michael Ruddick; He is a Chief architect in T-Force.

I will not forget to talk about Tareq Amin “The Vise president in T-Force”. He is a big fan of the new Metro Apps style; Yeah Metro is amazing.

Tareq is listening to me a lot, sharing Ideas, respecting people.
Also he is studying to make Google’s 80/20 Innovation Model part of our work; or maybe to do like Innvoation Center in Amman Office; That’s really coool!

Ok, Lets talk about some geeky stuff here. Watch the video to see the output; And If you like it; Continue reading and download the sample from link below. Smile

 

Download Sample

 

 

I’ve just created a simple control, I called it Pivot View Control.

Simply this control contains

PivotViewControl:
this control divided into three parts:
Title: Just a title for the Pivot Control (you can expose this property as a Dependency Property).
Header: Contains Items Control for Tabs (or Pivot Headers).
Content: Each header related with a UIElement which loaded in the Content Presenter.

<Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto" MinHeight="30"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <TextBlock TextWrapping="Wrap"
Text="Title, select one of items below"
VerticalAlignment="Center"
FontFamily="Segoe UI Semibold"
FontSize="21.333" Margin="7,0,0,0"/>
        <ListBox x:Name="PivotItemsHeaderList"
SelectedItem="{Binding SelectedItem, ElementName=PivotViewCtrl, 
Mode=TwoWay}"
                 Margin="0" Grid.Row="1"
Background="#00000000" BorderBrush="{x:Null}"
                 ItemsSource="{Binding Items, ElementName=PivotViewCtrl}"
                 ItemsPanel="{StaticResource ItemsPanelTemplate}"
                 ScrollViewer.VerticalScrollBarVisibility="Disabled"
                 ScrollViewer.HorizontalScrollBarVisibility="Disabled"
          ItemContainerStyle="{StaticResource ListBoxItemContainerStyle}"
                 FontFamily="Segoe WP SemiLight" FontSize="24"
                 ItemTemplate="{StaticResource DataTemplateListItem}"/>
      <ContentPresenter x:Name="ContentPresenter" Margin="0" Grid.Row="2">
          <ContentPresenter.Projection>
              <PlaneProjection CenterOfRotationX="0"/>
          </ContentPresenter.Projection>
      </ContentPresenter>
    </Grid>

The animation createed using Visual State Manager, In the next version I’m going to add many animation options. For now this animation is good Smile.

Simply you can change the animation using Expression Blend, Go to states tab in blend and change the animation.

I have two states in my control;

GoAway: will hide current displayed content. And this animation completed I will change the content of it.

ComeIn: When GoAway animation completed and content changed.  Start the animation to bring the new content.

Anim

This is the animation completed Event:

void StoryBoardGoAway_Completed(object sender, EventArgs e)
{
   string headerValue = this.PivotItemsHeaderList.SelectedItem.ToString();
   var pivotItem = this.ItemsSource
        .SingleOrDefault(p => p.Header == headerValue);
   this.ContentPresenter.Content = (UIElement)pivotItem.Content;
   VisualStateManager.GoToState(this, "ComeIn", true);
}

PivotItemControl.cs

This control is a dependency control; Its something like Tab Item.

public class PivotItemControl : DependencyObject
    {
        public DependencyProperty HeaderProperty =
            DependencyProperty.Register("Header", typeof(String),
            typeof(PivotItemControl), null);
         public String Header
        {
            get
            {
                return (String)GetValue(HeaderProperty);
            }
            set
            {
                SetValue(HeaderProperty, value);
            }
        }
        public DependencyProperty ContentProperty =
            DependencyProperty.Register("Content", typeof(UIElement),
            typeof(PivotItemControl), null);
         public UIElement Content
        {
            get
            {
                return (UIElement)GetValue(ContentProperty);
            }
            set
            {
                SetValue(ContentProperty, value);
            }
        }
    }

How to use it?

public MainPage()
        {
            InitializeComponent();
            this.Loaded += MainPage_Loaded;
        }
         void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            BindPivotItems();
            this.Loaded -= MainPage_Loaded;
        }
       private void BindPivotItems()
        {
          List<PivotItemControl> list = new List<PivotItemControl>();
          String[] headers = new string[] {"details","documents","notes"};
             foreach (string item in headers)
            {
                 list.Add(new PivotItemControl()
                        {
                            Header=item,
                            Content=new DummyContentControl() { Tag=item }
                        });
            }
           this.PivotViewControl.ItemsSource = list;
        }

Some enhancements could occurred; like using one Usercontrol as a Content; And Change the DataContent when GoAway Animation Completed. But I think you need to Expose an Event, Yeah that gonna be great.

Step by Step – Filtering List using Reactive Extensions (Rx Framework)


  1. Download Rx Framework from here.
  2. Install it.
  3. Create New Windows Forms project (you can choose WPF, Silverlight, WP7). I used WinForms in my example.
  4. Add TextBox and ListBox to it as you see below.

Screen shot 2011-07-14 at 1.50.46 AM

  5.   Add System.Reactive reference to your project (see image below).

Screen shot 2011-07-14 at 1.53.05 AM

6.  Create any dummy data (you can use any repository you want) and just to make things fast Smile

Screen shot 2011-07-14 at 1.51.58 AM

7. Add this chunk of code.

Screen shot 2011-07-14 at 1.52.17 AM

When TextChanged raised the subscribed code will be executed Asynchronously.

That’s it. As simple as that.

Good night. Smile

Dev-igner On Blend4


I just finished my preparation for the session.

Why Dev-igner? For me, I see Blend as a tool that helps developers & Designers (Maybe the proper name is frontend Developers) … anyway, I’m sure that WPF, Silverlight is rarely used in Jordan; Because of:

1. Learning Curve; and as always I hear & I say: There is no time; so the small biz companies prefer to stick with Win Forms.

2. More Effort will be consumed to add WOOW effect to the application.

3. And Who Should Learn Blend? Developers? But they Love Visual Studio and they prefer to stay with it. Designers?! Same thing, they prefer illustrator & Photoshop.

I agree with you both guys? But I’m sure that you did consume any moment to know that Blend is a great tool.

It’s easier to bind targets with source, Styling & templating, XAMLing things, also you can Import your .AI, .PSD, and FXG files, and more.

If we say that Dev. should work on Blend; then he will do a catastrophe. (Thanks to their ugly taste).

And the Designer cannot do the whole Blendy things a lone also.

Finally, I face such situation and I played the 2-Face character in batman (the ugly side is the dev part); XAMLing things with VS 2008 is a really Shit. Things are better with VS 2010 but not so much.
I forced to learn it and now I do not regret.

BLEND is the Best… I can imagine if Microsoft create such a tool for HTML5… I cannot wait for that moment.

Hoping you to enjoy my session:

The Event will be at 15 June 2011 in Sumayah University.

JorDev will keeps you updated insha Allah.

 

Silverlight training for Designers


Today I finished a special training which given by a Shane Morris.

About Shane Morris

Shane Morris is one of Australia’s most respected user experience professionals. Through consulting, mentoring and training he has helped organizations create compelling digital experiences since 1991. In that time he has worked on desktop applications, internet applications, mobile user interfaces, physical devices and web sites. Shane has taught user experience topics around the world and is also a key contributor to “101 Things I Learned in Interaction Design School” at ixd101.com.

Shane has worked with companies like Microsoft, Lonely Planet, M&C Saatchi, Cochlear, Amnesia Razorfish and Sensis – helping creative and technical professionals collaborate to create services that empower, inspire and reward. His passion is transforming the complex and constrained into the simple and powerful. Not just because it’s valuable endeavour, but because it’s hard – and therefore immensely rewarding.

Shane’s previous experience includes:

  • One of Microsoft’s first User Experience Evangelists world-wide.
  • Director of Echo Interaction Design for 6 years.
  • General Manager and Principal Consultant at The Hiser Group.

 

Its really awesome training. thank you AGENDA group and thank you Microsoft.

And special thanks for eSense to pay for that :).

Silverlight is not dead:


Yesterday, Scott Guthrie talks about the future of Silverlight.
There is a really nice new features in the next Silverlight release which will be at the end of 2011. (Beta version will be released in the first quarter of next the year 2011) .

Silverlight-5 comes with new 40 features (at least) and this some of it:

– New 3D APIs in Silverlight-5.

Silverlight-5 allows hardware video decoding support, which allows you to decode video on the GPU of a system. This enables smooth video streaming on lower end devices such as netbooks.

– With Silverlight-5; The developer can debug the binding expressions.

– Animations during Layout transitions

To see these features you can go to Scott’s blog post here and read more.

As I read before. Microsoft vision is to make Silverlight works on other devices like TVs, Microwave’s software, and on XBox also; And Windows Phone 7 is just a start of the evolution. so long Live Silverlight Smile.

RadControls for Windows Phone 7


Looool, I’ve got this mail from Telerik and I have a free WP7 RadControls license. I’m so happy for that.

I want to thank my TweetDeck Application for that Smile; It helps me to be one of the early people whom download the controls.

Hello Mohammad,

Thank you for downloading the CTP version of RadControls for Windows Phone.

I’m happy to let you know that as an early adopter you will receive a free license for RadControls for Windows Phone, when the product is officially released with Q1 2011. On the day of the release you will have the official version of RadControls for Windows Phone in the Download and Manage Your Products -> RadControls for Windows Phone 7 -> Product Downloads section of your Telerik account.

Our product team will greatly appreciate you feedback, do not hesitate to share it with them.

And if you are working on a Windows Phone application built with our controls, share it with us and we will help you promote it. For showcase submissions contact me directly.

Once again, thank you for your interest in our product and good luck with your development.

Ina Tontcheva

RadControls for Windows Phone Brand Manager

Telerik

http://www.telerik.com