Posts mit dem Label Controls werden angezeigt. Alle Posts anzeigen
Posts mit dem Label Controls werden angezeigt. Alle Posts anzeigen

Samstag, 17. Juli 2010

Silverlight: Drag & Drop Timing problems

If you use the new Silverlight 4 controls for drag&drop you have sometimes the problem that in your control the drag Event started even if you only select the next item in your listbox or treeview. The reason for this is that you maybe have long running events at your SelectedItem-Events. This forces the Drag&Drop-Container to raise a DragEnter-Event because the control does not become the MouseUp-Event in the desired time.

To solve this problem we made in our current project several approaches (Background process etc.). But the best way to reach your goal is a very “oldschool” method:

Using a timer to make your long running events  in the SelectedItem-Event

On SelectedItem Changed:
   1: var selectedEntryChangedTimer =
   2:                     new DispatcherTimer
   3:                         {
   4:                             Interval =
   5:                                 new TimeSpan(0, 0, 0, 0,
   6:                                             250)
   7:                         };
   8:                 selectedEntryChangedTimer.Tick += OnSelectedEntryChangedTimer;
   9:                 selectedEntryChangedTimer.Start();
 
Eventhandler:
   1: private void OnSelectedEntryChangedTimer(object o, EventArgs args)
   2: {
   3:     var timer = (DispatcherTimer)o;
   4:     timer.Stop();
   5:  
   6:     EventBroker.GetEvent<SelectedBilanzObjektChangedEvent>().Publish(
   7:         new SelectedBilanzObjektChangedEventArgs(_selectedEntry));
   8: }

Silverlight: Dataform Required field data UI customizing

If you use a dataform with the autogenerate property set to true, you can customize the behaviour and the look and feel of the dataform in the OnAutoGeneratingField Event.

For example you can set the look and feel of required fields to your individual style.

1. step: DataAnnotation for your properties
   1:  
   2:             [Display(Name = "WKN", ResourceType = typeof(DataResources), Order = 7)]
   3:             [StringLength(6, ErrorMessageResourceName = "ValidationErrorStringTooLong", ErrorMessageResourceType = typeof(ErrorResources))]
   4:             [Required(ErrorMessageResourceName = "ValidationErrorRequiredField", ErrorMessageResourceType = typeof(ErrorResources))]
   5:             public string WKN { get; set; }


2. step: Override OnAutoGeneratingField

Define your own DataForm control which inherits from DataForm. Then you can override the OnAutoGeneratingField method and do a lot of tricky things. Manipulating the behaviour of your autogenerated datafields or give them a different style. In my example we used in our project this mechanism to attach a GotFocus and LostFocus-Event to each TextBox and DatePicker, if there is a Required-Attribute defined.

   1: RequiredAttribute requiredAttribute = (RequiredAttribute)(from Attribute a in customAttributes 
   2:                   where a is RequiredAttribute select a).FirstOrDefault();
   3:  
   4: if (requiredAttribute != null) 
   5: {
   6:     if ((e.Field.Content is TextBox) || (e.Field.Content is DatePicker))
   7:     {
   8:         ((Control)e.Field.Content).GotFocus += new RoutedEventHandler(BilanzObjektDataForm_GotFocus);
   9:         ((Control)e.Field.Content).LostFocus += new RoutedEventHandler(BilanzObjektDataForm_LostFocus);
  10:     }
  11: }

The GotFocus- and LostFocus-Events looks like this:

   1: void BilanzObjektDataForm_LostFocus(object sender, RoutedEventArgs e)
   2:         {
   3:             // State auf "kein" Pflichtfeld setzen.
   4:             // State "normal" kann nicht definiert werden, da dieser eindeutig benamt werden muss
   5:             VisualStateManager.GoToState((Control)sender, "NotRequired", true);
   6:         }
   1: void BilanzObjektDataForm_GotFocus(object sender, RoutedEventArgs e)
   2:         {
   3:             // die Pflichtfeld-Behandlung gilt nur für TextBox- und DatePicker-Controls
   4:             // und nur dann, wenn der Text leer ist
   5:             if (((sender is TextBox) && (String.IsNullOrEmpty(((TextBox)sender).Text))) || 
   6:                 ((sender is DatePicker) && (String.IsNullOrEmpty(((DatePicker)sender).Text))))
   7:             {
   8:                 // State auf Pflichtfeld setzen
   9:                 VisualStateManager.GoToState((Control)sender, "Required", true);
  10:             }
  11:         }

In this Eventhandlers we use the VisualStateManager to give the controls, if the content is empty, a new visual state “Required” or in the other case the visual state “NotRequired”.

Now you have only to define the two new states in your style and you can enjoy the new Required field effect.

3. step: Add new States to your style

In the VisualStateManager section:

   1: <VisualStateGroup x:Name="RequiredStates">
   2:                                <VisualState x:Name="NotRequired">
   3:                                    <Storyboard>
   4:                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="PflichtfeldInfo">
   5:                                            <EasingDoubleKeyFrame KeyTime="0" Value="0"/>                                            
   6:                                        </DoubleAnimationUsingKeyFrames>
   7:                                    </Storyboard>
   8:                                </VisualState>
   9:                                <VisualState x:Name="Required">
  10:                                    <Storyboard>
  11:                                        <Storyboard>
  12:                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="PflichtfeldInfo">                                                
  13:                                                <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="1" >
  14:                                                    <EasingDoubleKeyFrame.EasingFunction>
  15:                                                        <CubicEase EasingMode="EaseOut"/>
  16:                                                    </EasingDoubleKeyFrame.EasingFunction>
  17:                                                </EasingDoubleKeyFrame>
  18:                                                <EasingDoubleKeyFrame KeyTime="0:0:2" Value="0"/>
  19:                                            </DoubleAnimationUsingKeyFrames>
  20:                                        </Storyboard>                                        
  21:                                    </Storyboard>
  22:                                </VisualState>
  23:                            </VisualStateGroup>

The content style:

   1: <Grid  >                              
   2:                                 <ScrollViewer x:Name="ContentElement" 
   3:                                               BorderThickness="0"
   4:                                               IsTabStop="False"
   5:                                               Margin="4,0,2,2"
   6:                                               VerticalAlignment="Center"  />
   7:                                 <TextBlock x:Name="PflichtfeldInfo"
   8:                                    Text="Pflichtfeld"
   9:                                    Style="{StaticResource PflichtfeldStyle}"
  10:                                    Opacity="0" />
  11:                             </Grid>                            
  12:                         </Grid>

Freitag, 2. Juli 2010

Silverlight Toolkit: Disable Drag for DragDropTarget

Each of the new DragDropTarget-Controls in the Silverlight Toolkit have a property AllowDrop which you can use to disable or enable the drop behaviour. But if you want to disable the underlaying controls to be draggable you have to cancel the drag start event at the ItemDragStarting Event:

1. Add Event handler
   1: <toolkit:TreeViewDragDropTarget ExpandNodeDelay="500"
   2:     AllowDrop="True" Drop="OnItemDropped" ItemDragStarting="TreeViewDragDropTarget_ItemDragStarting"
   3:     HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Top" >
2. Define the event handler
   1: private void TreeViewDragDropTarget_ItemDragStarting(object sender, ItemDragEventArgs e)
   2:         {
   3:             e.Cancel = true;
   4:             e.Handled = true;
   5:         }

Samstag, 29. Mai 2010

DataTemplate Selector with Silverlight

With Windows Presentation Foundation (WPF) it is very easy to switch your datatemplate in dependency of for example the type of data you want to bind. But Silverlight does not support this “Out-of-box”. So we have investigated some time in our current project to get a similar mechanism. The idea for my solution is, that we have a area in our form which should contains dynamic objects.One of the conditions for the solution was that it should support the common MVVM pattern.

The key point of my solution is to use a DependencyProperty and bind this to our viewmodel instance.

Step 1: Write a user control for the dynamic area which binds the view model
   1: <UserControl x:Class="SDX.FinancialPlanning.Views.Uebersicht.EinzelItemView"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
   6:     xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
   7:              xmlns:Detail1="clr-namespace:SDX.FinancialPlanning.ViewModels.Detail" 
   8:              xmlns:System="clr-namespace:System;assembly=mscorlib" xmlns:CommonHelpers="clr-namespace:SDX.FinancialPlanning.CommonHelpers" 
   9:              xmlns:Detail="clr-namespace:SDX.FinancialPlanning.Views.Detail" 
  10:              mc:Ignorable="d"
  11:     d:DesignHeight="300" d:DesignWidth="400">
  12:     
  13:     <Detail:DetailsViewSelector Content="{Binding SelectedEntryDetails}"  TemplateType="{Binding SelectedEntryDetails}"  >
  14:         <Detail:DetailsViewSelector.Resources>
  15:            <DataTemplate
  16:             x:Key="DetailWertpapierView"
  17:                         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  18:                         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  19:                         xmlns:Control="clr-namespace:SDX.FinancialPlanning.Views.Detail">
  20:                 <Control:DetailWertpapierView />
  21:             </DataTemplate>
  22:             <DataTemplate x:Key="EmptyTemplate">
  23:             </DataTemplate>
  24:         </Detail:DetailsViewSelector.Resources>
  25:         
  26:        
  27:     </Detail:DetailsViewSelector>
  28: </UserControl>

The DetailsViewSelector have two bindings

  • Content: Binds the view model to the choosen control
  • TemplateType: Binds also the view model to select the view by naming comventions

In my sample I habe only one DataTemplate and a empty Data Template if no corresponding objects could be bound.

Step 2: Implement the Detail View Selector
   1: <ContentPresenter x:Class="SDX.FinancialPlanning.Views.Detail.DetailsViewSelector"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
   6:                   Content="{Binding}">
   7:     <ContentPresenter.Resources>
   8:     </ContentPresenter.Resources>
   9: </ContentPresenter>

Code for the Control:

   1: public partial class DetailsViewSelector : ContentPresenter
   2:    {
   3:  
   4:        public static readonly DependencyProperty TemplateTypeProperty =
   5:        DependencyProperty.Register("TemplateType", typeof(DetailViewModelBase),
   6:        typeof(DetailsViewSelector), new PropertyMetadata(new DetailWertpapierViewModel()));
   7:  
   8:        public DetailViewModelBase TemplateType
   9:        {
  10:            get { return (DetailViewModelBase)GetValue(TemplateTypeProperty); }
  11:            set { SetValue(TemplateTypeProperty, value); }
  12:        }
  13:  
  14:        public DetailsViewSelector()
  15:        {
  16:            Loaded += new RoutedEventHandler(OnLoaded);
  17:        }
  18:  
  19:        private void OnLoaded(object sender, RoutedEventArgs e)
  20:        {
  21:            DetailViewModelBase templateType = TemplateType;
  22:            string viewName = templateType.GetType().FullName.Replace("Model", "");
  23:            Type viewType = Type.GetType(viewName);
  24:            if (viewType == null)
  25:            {
  26:                ContentTemplate = (DataTemplate) Resources["EmptyTemplate"];
  27:            }
  28:            else
  29:            {
  30:                DataTemplate dataTemplate = (DataTemplate)Resources[viewType.Name];
  31:                ContentTemplate = dataTemplate;
  32:            }
  33:        }
  34:    }

To have a very flexible mechanism the DetailsViewSelector works with naming conventions to choice the View for the given view model.