silverlight listbox autoscroll behavior

July 29, 2011 at 2:09 AMdsoltesz

Sometimes your list boxes can have a lot of items in them.  Even though you may order the list it still can be difficult for the user to find and item.  I like to give the user the abiltiy to search for an item in the listbox.  To keep this clean with MVVM, I created a behavior that can autoscroll for your via databinding.

 

Here is the behavior

 


/// <summary>
    /// Auto scroll listbox to item that is bound to the FoundItem property
    /// </summary>
    public class ListBoxItemAutoScrollBehavior : Behavior<ListBox>
    {
        #region Properties
        /// <summary>
        /// Item that is bound to listbox that you want to autoscroll to
        /// </summary>
        public object FoundItem
        {
            get { return GetValue(FoundItemProperty); }
            set { SetValue(FoundItemProperty, value); }
        }
 
        public static readonly DependencyProperty FoundItemProperty = DependencyProperty.Register("FoundItem"typeof(object), typeof(ListBoxItemAutoScrollBehavior), new PropertyMetadata(FoundItemChanged));
 
        private static void FoundItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((ListBoxItemAutoScrollBehavior)d).AssociatedObject.ScrollIntoView(e.NewValue);
        }
        #endregion
    }

 

Make sure you add a reference to System.Windows.Interactivity.

 

To use the behavior I do something like this.

<ListBox ItemsSource="{Binding MyItems}"  DisplayMemberPath="ItemName"  SelectionMode="Multiple">
                        <i:Interaction.Behaviors>
                            <behaviors:ListBoxItemAutoScrollBehavior FoundItem="{Binding FoundItem}"/>
                        </i:Interaction.Behaviors>
                    </ListBox>
<TextBox Text="{Binding ItemToFind, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" BorderThickness="1" />

 

Now in your view model just define your properties to bind to.


public ObservableCollection<Item> MyItems
        {
            get { return _myItems; }
            set
            {
 
                if (_myItems == valuereturn;
                _myItems = value;
                OnPropertyChanged(() => MyItems);
            }
        }
 
        public string ItemToFind
        {
            get { return _itemToFind; }
            set
            {
                if (_itemToFind == valuereturn;
                _itemToFind = value;
                 FoundItem = MyItems.FirstOrDefault(o => o.ItemName.StartsWith(_itemToFind, StringComparison.CurrentCultureIgnoreCase));
                OnPropertyChanged(() => ItemToFind);
                OnPropertyChanged(() => FoundItem);
            }
        }
         public Item FoundItem { getset; }

 Now when the user types in something into the textbox, it will autoscroll to the item that is found

Posted in: behaviors | c# | silverlight

Tags: , ,

ListBox load data when user reaches end of list

January 25, 2011 at 10:48 PMdsoltesz

Great post on a behavior that will raise a command when the users scrolls to the end of a listbox to fetch more data.

This can easily be applied to wpf or silverlight as well.

http://danielvaughan.orpius.com/post/Scroll-Based-Data-Loading-in-Windows-Phone-7.aspx

Posted in: .NET | behaviors | silverlight

Tags: , ,

silverlight attach behavior in code

October 27, 2010 at 9:51 PMdsoltesz

I had a scenario where I needed to build part of the UI completely from Code and wanted to include a behavior as part of one of the UI controls.

Lets walk through and example of how to do this.

I want to create a WrapPanel that has the FluidMoveBehavior associated to the WrapPanel and applies the behavior to all its children.



var panel = new WrapPanel
{
   HorizontalAlignment = HorizontalAlignment.Left
};

var timeSpan = new TimeSpan(0, 0, 0, 0, 400);
var ease = new CubicEase
{
    EasingMode = EasingMode.EaseIn
};
var fluidMoveBehavior = new FluidMoveBehavior
{
    AppliesTo = FluidMoveScope.Children,
    Duration = new Duration(timeSpan),
    EaseX = ease,
    EaseY = ease
};

System.Windows.Interactivity.Interaction.GetBehaviors(panel).Add(fluidMoveBehavior);

 

Pretty easy, just make sure you add references to

  • System.Windows.Interactivity
  • Microsoft.Expression.Effects
  • Microsoft.Express.Interactions

Posted in: .NET | behaviors | c# | silverlight

Tags: , ,

Silverlight datagrid double click behavior MVVM

October 1, 2010 at 6:20 PMdsoltesz

I originally created a behavior to add double click support to a silverlight datagrid.  Post here http://www.dansoltesz.com/post/2010/02/19/Silverlight-datagrid-double-click-behavior.aspx

I had someone ask me how they could get this to work with MVVM so I want to post the changes to the DataGridDoubleClickBehavior class in the original post to support commanding.

Add a new dependency property

public ICommand DoubleClickCommand
        {
            get { return (ICommand)GetValue(DoubleClickCommandProperty); }
            set { SetValue(DoubleClickCommandProperty, value); }
        }

        public static readonly DependencyProperty DoubleClickCommandProperty = DependencyProperty.Register("DoubleClickCommand"typeof(ICommand), typeof(DataGridDoubleClickBehavior), new PropertyMetadata(DoubleClickCommandChanged));

        private static void DoubleClickCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Code for dealing with your property changes
        }


 public object CommandParameter
        {
            get { return (object)GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }

        public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter"typeof(object), typeof(DataGridDoubleClickBehavior), new PropertyMetadata(CommandParameterChanged));

        private static void CommandParameterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Code for dealing with your property changes
        }

Now in the _gridClickManager_DoubleClick event defined in the behavior add this new line

 void _gridClickManager_DoubleClick(object sender, MouseButtonEventArgs e)
        {
            if (DoubleClick != null)
                DoubleClick(sender, e);

           if(DoubleClickCommand != null) DoubleClickCommand.Execute(CommandParameter); //code added

        } 

the behavior class now looks like this

 public class DataGridDoubleClickBehavior : Behavior<DataGrid>
    {

        private readonly MouseClickManager _gridClickManager;
        public event EventHandler<MouseButtonEventArgs> DoubleClick;
        public object CommandParameter
        {
            get { return (object)GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }

        public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter"typeof(object), typeof(DataGridDoubleClickBehavior), new PropertyMetadata(CommandParameterChanged));

        private static void CommandParameterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Code for dealing with your property changes
        }
        public ICommand DoubleClickCommand
        {
            get { return (ICommand)GetValue(DoubleClickCommandProperty); }
            set { SetValue(DoubleClickCommandProperty, value); }
        }

        public static readonly DependencyProperty DoubleClickCommandProperty = DependencyProperty.Register("DoubleClickCommand"typeof(ICommand), typeof(DataGridDoubleClickBehavior), new PropertyMetadata(DoubleClickCommandChanged));

        private static void DoubleClickCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Code for dealing with your property changes
        }

        public DataGridDoubleClickBehavior()
        {
            _gridClickManager = new MouseClickManager(300);
            _gridClickManager.DoubleClick += new MouseButtonEventHandler(_gridClickManager_DoubleClick);
        }

        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.LoadingRow += OnLoadingRow;
            AssociatedObject.UnloadingRow += OnUnloadingRow;
        }

        void OnUnloadingRow(object sender, DataGridRowEventArgs e)
        {
            //row is no longer visible so remove double click event otherwise
            //row events will miss fire
            e.Row.MouseLeftButtonUp -= _gridClickManager.HandleClick;
        }

        void OnLoadingRow(object sender, DataGridRowEventArgs e)
        {
            //row is visible in grid, wire up double click event
            e.Row.MouseLeftButtonUp += _gridClickManager.HandleClick;
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            AssociatedObject.LoadingRow -= OnLoadingRow;
            AssociatedObject.UnloadingRow -= OnUnloadingRow;
        }

        void _gridClickManager_DoubleClick(object sender, MouseButtonEventArgs e)
        {
            if (DoubleClick != null)
                DoubleClick(sender, e);
 if (DoubleClickCommand != null)
DoubleClickCommand.Execute(CommandParameter);
        }

    }

 

Now in your datagrid instead of wiring up an event for the double click you bind to the DoubleClickCommand of your viewmodel.

Originally was

<data:DataGrid x:Name="myDataGrid">

<i:Interaction.Behaviors>

            <behaviors:DataGridDoubleClickBehavior DoubleClick="myDataGrid_DoubleClick"/>

      </i:Interaction.Behaviors> 

</data:DataGrid>

Now change to

That its, now when a user double clicks a grid row, the command will be raised in your viewModel.

<data:DataGrid x:Name="myDataGrid">

<i:Interaction.Behaviors>

            <behaviors:DataGridDoubleClickBehavior

DoubleClickCommand="{Binding DoubleCommand}

CommandParameter="{Binding SelectedItem, ElementName=myDataGrid}"/>

 

    </i:Interaction.Behaviors>

</data:DataGrid>

 

Posted in: .NET | behaviors | silverlight

Tags: , , ,

Silverlight 4 textbox right click context menu with cut, copy and paste behavior

May 27, 2010 at 9:46 PMdsoltesz

I wanted to be able to allow my users the ability to right click in a textbox and have a contextmenu shown to be able to cut, copy and paste. I decided to create a behavior so that the functionality can easily be added to any textbox control.


using System.Windows;

using System.Windows.Controls;

using System.Windows.Input;

using System.Windows.Interactivity;

 

namespace MyApp.Behaviors.TextBox

{

    public class TextBoxCutCopyPasteBehavior : Behavior<System.Windows.Controls.TextBox>

    {

        private ContextMenu _contextMenu;

        private MenuItem _copy;

        private MenuItem _cut;

        private MenuItem _paste;

        public TextBoxCutCopyPasteBehavior()

        {

 

            _contextMenu = new ContextMenu();

            _cut = new MenuItem();

            _cut.Header = "Cut";

            _cut.Click += new RoutedEventHandler(cut_Click);

            _contextMenu.Items.Add(_cut);

 

            _copy = new MenuItem();

            _copy.Header = "Copy";

            _copy.Click += new RoutedEventHandler(copy_Click);

            _contextMenu.Items.Add(_copy);

 

            _paste = new MenuItem();

            _paste.Header = "Paste";

            _paste.Click += new RoutedEventHandler(paste_Click);

            _contextMenu.Items.Add(_paste);

 

 

        }

 

        void paste_Click(object sender, RoutedEventArgs e)

        {

            AssociatedObject.SelectedText = Clipboard.GetText();

            _contextMenu.IsOpen = false;

        }

 

        void cut_Click(object sender, RoutedEventArgs e)

        {

            Clipboard.SetText(AssociatedObject.SelectedText);

            AssociatedObject.SelectedText = string.Empty;

            AssociatedObject.Focus();

            _contextMenu.IsOpen = false;

        }

 

        void copy_Click(object sender, RoutedEventArgs e)

        {

            Clipboard.SetText(AssociatedObject.SelectedText);

            AssociatedObject.Focus();

            _contextMenu.IsOpen = false;

        }

 

        protected override void OnAttached()

        {

 

            AssociatedObject.MouseRightButtonDown += new MouseButtonEventHandler(AssociatedObject_MouseRightButtonDown);

            AssociatedObject.MouseRightButtonUp += new MouseButtonEventHandler(AssociatedObject_MouseRightButtonUp);

            AssociatedObject.SetValue(ContextMenuService.ContextMenuProperty, _contextMenu);

            base.OnAttached();

        }

 

        void AssociatedObject_MouseRightButtonUp(object sender, MouseButtonEventArgs e)

        {

            if (Clipboard.ContainsText())

                _paste.IsEnabled = true;

            else

                _paste.IsEnabled = false;

 

            if (string.IsNullOrEmpty(AssociatedObject.SelectedText))

            {

                _cut.IsEnabled = false;

                _copy.IsEnabled = false;

            }

            else

            {

                _cut.IsEnabled = true;

                _copy.IsEnabled = true;

            }

            _contextMenu.IsOpen = true;

 

        }

 

        void AssociatedObject_MouseRightButtonDown(object sender, MouseButtonEventArgs e)

        {

            e.Handled = true;

        }

 

        protected override void OnDetaching()

        {

            AssociatedObject.MouseRightButtonDown -= new MouseButtonEventHandler(AssociatedObject_MouseRightButtonDown);

            AssociatedObject.MouseRightButtonUp -= new MouseButtonEventHandler(AssociatedObject_MouseRightButtonUp);

            _contextMenu = null;

            base.OnDetaching();

 

        }

 

    }

}

 

 

Once the behavior is created we can now add to our TextBox

  1. add a reference to System.Windows.Interactivity
  2. In your xaml code add
    1. xmlns

      :i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    2. xmlns

      :behaviors="clr-namespace:MyApp.Behaviors.TextBox;assembly=MyApp"
    3.  <TextBox x:Name="myTextBox" >
             <i:Interaction.Behaviors>
                 <behaviros:TextBoxCutCopyPasteBehavior/>
             </i:Interaction.Behaviors>
         </TextBox>


That's it, now you have right click support for your textbox with cut, copy and paste.

Posted in: behaviors | silverlight

Tags: ,

Silverlight datagrid double click behavior

February 19, 2010 at 4:56 PMdsoltesz

I wanted to allow users to be able to double click on a grid row and have some functionality performed.  At first I was using a double click manager class from http://weblogs.asp.net/aboschin/archive/2008/03/17/silverlight-2-0-a-double-click-manager.aspx 

I was typing same code through out my app to attach the double click code to my datagrids.  I decided to refactor this functionality into a behavior.

Here is double click manager class

/// <summary>

/// Class to attach mouse click events to UIElements

/// </summary>

    public class MouseClickManager

    {

        #region Private members

        private event MouseButtonEventHandler _click;

        private event MouseButtonEventHandler _doubleClick;

        #endregion

        #region Constructor

        /// <summary>

        /// Initializes a new instance of the <see cref="MouseClickManager"/> class.

        /// </summary>

        /// <param name="control">The control.</param>

        public MouseClickManager(int doubleClickTimeout)

        {

            this.Clicked = false;

            this.DoubleClickTimeout = doubleClickTimeout;

        }

        #endregion

        #region Events

        public event MouseButtonEventHandler Click

        {

            add { _click += value; }

            remove { _click -= value; }

        }

        public event MouseButtonEventHandler DoubleClick

        {

            add { _doubleClick += value; }

            remove { _doubleClick -= value; }

        }

        /// <summary>

        /// Called when [click].

        /// </summary>

        /// <param name="sender">The sender.</param>

        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>

        private void OnClick(object sender, MouseButtonEventArgs e)

        {

            if (_click != null)

            {

                Debug.Assert(sender is Control);

                (sender as Control).Dispatcher.BeginInvoke(_click, sender, e);

            }

        }

        /// <summary>

        /// Called when [double click].

        /// </summary>

        /// <param name="sender">The sender.</param>

        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>

        private void OnDoubleClick(object sender, MouseButtonEventArgs e)

        {

            if (_doubleClick != null)

            {

                _doubleClick(sender, e);

            }

        }

        /// <summary>

        /// Handles the click.

        /// </summary>

        /// <param name="sender">The sender.</param>

        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>

        public void HandleClick(object sender, MouseButtonEventArgs e)

        {

            lock (this)

            {

                if (this.Clicked)

                {

                    this.Clicked = false;

                    OnDoubleClick(sender, e);

                }

                else

                {

                    this.Clicked = true;

                    ParameterizedThreadStart threadStart = new ParameterizedThreadStart(ResetThread);

                    Thread thread = new Thread(threadStart);

                    thread.Start(e);

                }

            }

        }

        #endregion

        #region Properties

        /// <summary>

        /// Gets or sets a value indicating whether this <see cref="MouseClickManager"/> is clicked.

        /// </summary>

        /// <value><c>true</c> if clicked; otherwise, <c>false</c>.</value>

        private bool Clicked { get; set; }

        /// <summary>

        /// Gets or sets the timeout.

        /// </summary>

        /// <value>The timeout.</value>

        public int DoubleClickTimeout { get; set; }

        #endregion

        #region Methods

        #region Private

        /// <summary>

        /// Resets the thread.

        /// </summary>

        /// <param name="state">The state.</param>

        private void ResetThread(object state)

        {

            Thread.Sleep(this.DoubleClickTimeout);

 

            lock (this)

            {

                if (this.Clicked)

                {

                    this.Clicked = false;

                    OnClick(this, (MouseButtonEventArgs)state);

                }

            }

        }

        #endregion

        #endregion

    }

I then used that class to create my datagrid double click behavior

public class DataGridDoubleClickBehavior : Behavior<DataGrid>

    {

        private readonly MouseClickManager _gridClickManager;

        public event EventHandler<MouseButtonEventArgs> DoubleClick;

        public DataGridDoubleClickBehavior()

        {

            _gridClickManager = new MouseClickManager(300);

            _gridClickManager.DoubleClick += new MouseButtonEventHandler(_gridClickManager_DoubleClick);

        }

        protected override void OnAttached()

        {

            base.OnAttached();

            AssociatedObject.LoadingRow += OnLoadingRow;

            AssociatedObject.UnloadingRow += OnUnloadingRow;

        }

        void OnUnloadingRow(object sender, DataGridRowEventArgs e)

        {

            //row is no longer visible so remove double click event otherwise

            //row events will miss fire

            e.Row.MouseLeftButtonUp -= _gridClickManager.HandleClick;

        }

        void OnLoadingRow(object sender, DataGridRowEventArgs e)

        {

            //row is visible in grid, wire up double click event

            e.Row.MouseLeftButtonUp += _gridClickManager.HandleClick;

        }

        protected override void OnDetaching()

        {

            base.OnDetaching();

            AssociatedObject.LoadingRow -= OnLoadingRow;

            AssociatedObject.UnloadingRow -= OnUnloadingRow;

        }

        void _gridClickManager_DoubleClick(object sender, MouseButtonEventArgs e)

        {

            if (DoubleClick != null)

                DoubleClick(sender, e);

        }

    }

Once the behavior is created we can now add to our datagrid

  1. add a reference to System.Windows.Interactivity
  2. In your xaml code add
    1. xmlns

      :i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    2. xmlns

      :behaviors="clr-namespace:MyApp.Behaviors;assembly=MyApp"
    3. <data:DataGrid x:Name="myDataGrid">

      <i:Interaction.Behaviors>

                  <behaviors:DataGridDoubleClickBehavior DoubleClick="myDataGrid_DoubleClick"/>

            </i:Interaction.Behaviors>

      </data:DataGrid>

In your code behind, handle the double click event

private void MyDatagrid_DoubleClick(object sender, MouseButtonEventArgs e)

{

      //do something      

}

That's it, now you have double click events for your data grid

Posted in: .NET | c# | silverlight | behaviors

Tags: , , , ,