Freitag, 30. Juli 2010

Silverlight: Set Focus to selected item in Listbox

If you want to set the focus to the selected item in your Silverlight Listbox Control you have to use this dirty trick:

   1: int selectedIndex = itemsListBox.SelectedIndex;
   2:                itemsListBox.SelectedIndex = 0;
   3:                itemsListBox.Focus();
   4:                itemsListBox.SelectedIndex = selectedIndex;

Silverlight: DataGrid and Comboxbox with MVVM

If you are using a MVVM pattern with a view model you have generally the problem to bind a combobox inside a Datagrid because you cannot access the data context of your view by referencing with the elementname syntax. The trick to bind your Comboxbox inside a DataGridTemplateColumn is to define the reference to your viewmodel with a StaticResoucre and use this for the DataContext-Binding and combobox-Binding:

StaticResoucre of the viewmodel
   1:  
   2:     <controls:ChildWindow.Resources>
   3:         <ViewModels:SettingsViewModel x:Key="SettingsViewModel" />
   4:     </controls:ChildWindow.Resources>
Binding the view to your viewmodel
   1: <Grid x:Name="LayoutRoot" Margin="2" DataContext="{StaticResource SettingsViewModel}">
Combobox-Binding in the Datagrid
   1: <sdk:DataGridTemplateColumn.CellEditingTemplate>
   2:     <DataTemplate>
   3:         <ComboBox ItemsSource="{Binding Gesellschaften,Source={StaticResource SettingsViewModel}}" 
   4:                   DisplayMemberPath="Name" SelectedValue="{Binding GesellschaftId, Mode=TwoWay}" 
   5:                   SelectedValuePath="GesellschaftId"  />
   6:     </DataTemplate>
   7: </sdk:DataGridTemplateColumn.CellEditingTemplate>

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>

Samstag, 10. Juli 2010

RIA Services: Windows Phone 7 and SOAP endpoint

You can use RIA services as a normal WCF service with several clients. For example as Excel OData-Source or as data delivery service for your Windows Phone 7 application. To achieve this goal you have to install the latest RIA Services Toolkit and do a little configuration work.

Reference for Microsoft.ServiceModel.DomainServices.Hosting

Add a reference of Microsoft.ServiceModel.DomainServices.Hosting to your RIA Services project.

It is important that you choose the version which was shipped by the RIA Services (Silverlight) toolkit. The standard standard assembly from the SDK does not contain the SoapXmlEndpointFactory and JSonEndpointFactory.

So take this one:

image

For the OData endpoint you need the System.ServiceModel.DomainServices.Hosting.OData assembly from the SDK directory.

Configure the endpoints

Configure the Json, Soap and OData endpoints in the web.config:

<system.serviceModel>
      <domainServices>
        <endpoints>
          <add name="Soap" 
               type="Microsoft.ServiceModel.DomainServices.Hosting.SoapXmlEndpointFactory, Microsoft.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
          <add name="Json" 
               type="Microsoft.ServiceModel.DomainServices.Hosting.JsonEndpointFactory, Microsoft.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
          <add name="OData"
               type="System.ServiceModel.DomainServices.Hosting.ODataEndpointFactory, System.ServiceModel.DomainServices.Hosting.OData, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
        </endpoints>
      </domainServices>
       <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
     </system.serviceModel>
Using of RIA Services in your Windows Phone 7 application

To use the rias service (Soap endpoint) in your Windows Phone 7 application you have only to add a new reference and enter the Uri of your ria service.

Your Uri consists of the <namespace of your ria service>-<classname of your ria service>.svc where “.” are replaced with “-“ !

image

 

Now you can create a proxy client and access to your Domainservice methods:

   1: public partial class MainPage : PhoneApplicationPage
   2:     {
   3:         object _selectedItem;
   4:         PrivatbilanzService.FinancialPlanningDomainServiceSoapClient client;
   5:  
   6:         public MainPage()
   7:         {
   8:             InitializeComponent();
   9:  
  10:             client = new PrivatbilanzService.FinancialPlanningDomainServiceSoapClient();
  11:  
  12:             SupportedOrientations = SupportedPageOrientation.Portrait;
  13:             Loaded += new RoutedEventHandler(MainPage_Loaded);
  14:  
  15:             PageTransitionList.Completed += new EventHandler(PageTransitionList_Completed);
  16:  
  17:             // Set the data context of the listbox control to the sample data
  18:             DataContext = new MainViewModel();
  19:         }

 

Uri of all endpoints

The Uri for our exposed endpoints are:

OData : http://localhost:[portnumber]/<namespace of your ria service>-<classname of your ria service>.svc/OData/

SOAP: http://localhost:[portnumber]/<namespace of your ria service>-<classname of your ria service>.svc

JSON: http://localhost:[portnumber]/<namespace of your ria service>-<classname of your ria service>.svc/JSON/

With these exposed endpoints, you can talk to multiple clients:

  • Excel Power pivot - Using the OData endpoint
  • Windows Phone 7 – Using the SOAP endpoint
  • AJAX client – Using the JSON endpoint

Samstag, 3. Juli 2010

Silverlight: Converter with multiple parameters

Sometimes it is necessary that you have a converter (interface IValueConverter) with multiple parameters. To reach this goal, you can use a DependencyProperty. That’s normally a very easy step, but I spend some extraordinary time with my converter becuase in my case the second parameter should be bindable

So what was the problem?

I have tried to derive the Converter from UserControl – great mistake: You have to use FrameworkElement instead!

Usage:
   1: <Helpers:StatisticsConverter x:Key="StatisticsConverter" ViewModel="{Binding ElementName=LayoutRoot,Path=DataContext.Statistics}" />
   2:     
   3: <TextBlock Margin="0,0,10,0"
   4:    FontSize="9"  
   5:    x:Name="InfoSummeText" Text="{Binding Converter={StaticResource StatisticsConverter}, ConverterParameter=Summe}" >
   6: </TextBlock>
Converter:
   1: public class StatisticsConverter : FrameworkElement, IValueConverter
   2:    {
   3:  
   4:        #region IValueConverter Members
   5:  
   6:        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
   7:        {
   8:            var statisticsViewModel = (StatisticsViewModel)ViewModel;
   9:            var statistics = statisticsViewModel.GetAnzeigeKategorieStatistics(value as AnzeigeKategorie);
  10:            switch (parameter.ToString())
  11:            {
  12:                case "Summe":
  13:                    {
  14:                        return String.Format("{0:N2}", statistics.Summe) + " EUR";
  15:                    }
  16:                default:
  17:                    {
  18:                        throw new NotImplementedException(parameter as string);
  19:                    }
  20:            }
  21:        }
  22:  
  23:        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  24:        {
  25:            return "";
  26:        }
  27:  
  28:        #endregion
  29:  
  30:        #region object ViewModel (DependencyProperty)
  31:  
  32:        public object ViewModel
  33:        {
  34:            get { return (object)GetValue(ViewModelProperty); }
  35:            set { SetValue(ViewModelProperty, value); }
  36:        }
  37:  
  38:        public static readonly DependencyProperty ViewModelProperty =
  39:            DependencyProperty.Register(
  40:                "ViewModel",
  41:                typeof(object),
  42:                typeof(StatisticsConverter),
  43:                new PropertyMetadata(null, null));
  44:  
  45:  
  46:        #endregion ViewModel (DependencyProperty)
  47:    }