lunedì 28 novembre 2011

Silverlight - Simulazione navigazione dati (stile Access)

Silverlight 4.0

Nella mia applicazione avevo la necessità di navigare tra i dati di una child window, stile Access tanto per intenderci.

Questa la view in questione, che ha il compito di gestire i Cespiti in carico.



Al loaded della view viene eseguito il binding dell’oggetto Cespite recuperato tramite un servizio WCF RIA Service. Nel mio caso il DocumentContext è il contesto al servizio che gestisce tutto il documento: ritenute d’acconto, ratei/risconti e cespiti compresi.
readonly DocumentoContext _documentoContext;

Cespite cespite = _documento.Cespite.Count > 0 ? _documento.Cespite.FirstOrDefault()
                                                : new Cespite();

if (_documento.Cespite.Count == 0)
   _documento.Cespite.Add(cespite);

LayoutRoot.DataContext = cespite;

A seconda se ne esistono già viene caricato il primo della lista o ne viene creato uno nuovo (in questo modo poi possiamo fare a meno del pulsante Nuovo nella toolbar).
Per passare quindi al cespite successivo o crearne uno di nuovo basta premere il pulsante con la freccia verso destra, al quale è associato il seguente evento:

private void NextCespiteClicked(object sender, MouseButtonEventArgs e)
{
      _index += 1;

      if (_index > _documentoContext.Cespites.Count - 1)
      {
          // Non ci sono cespiti, ne viene aggiunto quindi uno al contesto.
          Cespite cespite = new Cespite();
          _documento.Cespite.Add(cespite);

         // Binding del cespite appena creato alla view
         LayoutRoot.DataContext = cespite;
     }
     Else
         // Viene eseguito il binding del cespite dell'indice corrispondente.
         LayoutRoot.DataContext = _documentoContext.Cespites.ElementAt(_index);
}

Invece per andare all’elemento precedente basta premere il pulsante con la freccia verso sinistra al quale è associate il seguente evento:

private void PreviousCespiteClicked(object sender, MouseButtonEventArgs e)
{        
     if(_index >= 1)
     {
          _index -= 1;
          LayoutRoot.DataContext = _documentoContext.Cespites.ElementAt(_index);
     }
}

Chiaramente è un meccanismo alquanto semplice, abbastanza efficace, che può essere esteso a proprio piacimento.

giovedì 6 ottobre 2011

IIS 7.0 Manager Tool - "The server is not accepting remote connections"


IIS 7.0 Manager Tool

Collegandomi  ad un sito remoto tramite il tool IIS Manager ottengo il seguente messaggio d’errore: “The server is not accepting remote connections”



Ho risolto disabilitando il flag “Utilizza un server proxy per le connessione LAN” tra la opzioni di Internet Explorer – Connessioni – Impostazioni LAN


mercoledì 21 settembre 2011

WPF - DataGrid (DeferRefresh exception)

C# 4.0
In una view con all’interno una DataGrid mi capitava a volte, aggiungendo o modificando una riga, la seguente exception:
"’DeferRefresh’ is not allowed during an AddNew or EditItem transaction"

Girando quindi per la rete ho trovato un behaviour che ho poi adattato alle mie esigenze, in pratica al lost focus della grid viene forzato un CommitNew o un CommitEdit a seconda del caso.

A seguire il behaviour e la relativa modifica allo XAML.

using System.ComponentModel;
using System.Windows.Input;
using System.Windows;
using System.Windows.Controls;

namespace Admin.Console.Behaviour
{
    class DataGridCommitOnUnfocusedBehaviour
    {
        #region DataGridCommitOnUnfocusedBehaviour

        public static bool GetDataGridCommitOnUnfocused(DataGrid datagrid)
        {
            return (bool)datagrid.GetValue(DataGridCommitOnUnfocusedProperty);
        }

        public static void SetDataGridCommitOnUnfocused(
         DataGrid datagrid, bool value)
        {
            datagrid.SetValue(DataGridCommitOnUnfocusedProperty, value);
        }

        public static readonly DependencyProperty DataGridCommitOnUnfocusedProperty =
            DependencyProperty.RegisterAttached(
            "DataGridCommitOnUnfocused",
            typeof(bool),
            typeof(DataGridCommitOnUnfocusedBehaviour),
            new UIPropertyMetadata(false, OnDataGridCommitOnUnfocusedChanged));

        static void OnDataGridCommitOnUnfocusedChanged(
         DependencyObject depObj, DependencyPropertyChangedEventArgs e)
        {
            DataGrid datagrid = depObj as DataGrid;
            if (datagrid == null)
                return;

            if (e.NewValue is bool == false)
                return;

            if ((bool)e.NewValue)
            {
                datagrid.LostKeyboardFocus += CommitDataGridOnLostFocus;
                datagrid.DataContextChanged += CommitDataGridOnDataContextChanged;
            }
            else
            {
                datagrid.LostKeyboardFocus -= CommitDataGridOnLostFocus;
                datagrid.DataContextChanged -= CommitDataGridOnDataContextChanged;
            }
        }

        static void CommitDataGridOnLostFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            DataGrid senderDatagrid = sender as DataGrid;

            if (senderDatagrid == null)
                return;

            UIElement focusedElement = Keyboard.FocusedElement as UIElement;
            if (focusedElement == null)
                return;

            DataGrid focusedDatagrid = GetParentDatagrid(focusedElement); //let's see if the new focused element is inside a datagrid

            if (focusedDatagrid == senderDatagrid)
            {
                return;
                //if the new focused element is inside the same datagrid, then we don't need to do anything;
                //this happens, for instance, when we enter in edit-mode: the DataGrid element loses keyboard-focus, which passes to the selected DataGridCell child
            }

            //otherwise, the focus went outside the datagrid; in order to avoid exceptions like ("DeferRefresh' is not allowed during an AddNew or EditItem transaction")
            //or ("CommitNew is not allowed for this view"), we undo the possible pending changes, if any

            IEditableCollectionView collection = senderDatagrid.Items;
            if (collection.IsEditingItem)
            {
                collection.CommitEdit();
            }
            else if (collection.IsAddingNew)
            {
                collection.CommitNew();
            }
        }

        private static DataGrid GetParentDatagrid(UIElement element)
        {
            UIElement childElement; //element from which to start the tree navigation, looking for a Datagrid parent

            if (element is ComboBoxItem) //since ComboBoxItem.Parent is null, we must pass through ItemsPresenter in order to get the parent ComboBox
            {
                ItemsPresenter parentItemsPresenter = VisualTreeFinder.FindParentControl<ItemsPresenter>((element as ComboBoxItem));
                ComboBox combobox = parentItemsPresenter.TemplatedParent as ComboBox;
                childElement = combobox;
            }
            else
            {
                childElement = element;
            }

            DataGrid parentDatagrid = VisualTreeFinder.FindParentControl<DataGrid>(childElement); //let's see if the new focused element is inside a datagrid
            return parentDatagrid;
        }

        static void CommitDataGridOnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            DataGrid senderDatagrid = sender as DataGrid;

            if (senderDatagrid == null)
                return;

            IEditableCollectionView collection = senderDatagrid.Items;

            if (collection.IsEditingItem)
            {
                collection.CommitEdit();
            }
            else if (collection.IsAddingNew)
            {
                collection.CommitNew();
            }
        }

        #endregion DataGridCommitOnUnfocusedBehaviour
    }
}

class VisualTreeFinder
    {
         /// <summary>
        /// Find a specific parent object type in the visual tree
        /// </summary>
        public static T FindParentControl<T>(DependencyObject outerDepObj) where T : DependencyObject
        {
            DependencyObject dObj = VisualTreeHelper.GetParent(outerDepObj);
            if (dObj == null)
                return null;

            if (dObj is T)
                return dObj as T;

            while ((dObj = VisualTreeHelper.GetParent(dObj)) != null)
            {
                if (dObj is T)
                    return dObj as T;
            }

            return null;
        }
    }

Nello XAML ho introdotto il seguente attributo evidenziato in giallo:

<DataGrid AutoGenerateColumns="False" Height="200" HorizontalAlignment="Left" Margin="132,43,0,0" VerticalAlignment="Top" Width="400"
                                            ItemsSource="{Binding Threshold.Hierarchies, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
behaviour:DataGridCommitOnUnfocusedBehaviour.DataGridCommitOnUnfocused="True">
                                            <DataGrid.RowValidationRules>
                                                <Common:RowDataInfoValidationRule ValidationStep="UpdatedValue" />
                                            </DataGrid.RowValidationRules>
                                            <DataGrid.Columns>
                                                <DataGridTextColumn Width="190" Binding="{Binding Path=Code, UpdateSourceTrigger=PropertyChanged}"  EditingElementStyle="{StaticResource CellEditStyle}"
                                                 Header="{Binding Source={StaticResource CustomResources}, Path=LocalizedStrings.grdThresholdHiearchyCode}"/>
                                                <DataGridCheckBoxColumn Width="190" Binding="{Binding Path=Included, UpdateSourceTrigger=PropertyChanged}"
                                                 Header="{Binding Source={StaticResource CustomResources}, Path=LocalizedStrings.grdThresholdHiearchyIncluded}"/>
                                            </DataGrid.Columns>
                                        </DataGrid>




mercoledì 24 agosto 2011

Silverlight - Tooltip su dei giorni particolari di un controllo Calendar

Silverlight 4.0

L’esigenza è quella ottenere un tooltip su alcuni giorni particolari, evidenziati in rosso, di un oggetto Calendar, ad esempio per evidenziare le proprie scadenze fiscali; e questa è la soluzione che ho adottato.
Come prima cosa ho creato i seguenti converter, uno per i giorni in “rosso” e l’altro per il tooltip:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;

namespace ContabilitaSelfService.Converter
{
    public class BackgroundDayCalendarConverter : IValueConverter
    {
        //public static IList<DateTime> Holidays { get; set; }
        public static IList<DateTime> Evidences { get; set; }

        public object Convert(object value,
                            Type targetType,
                            object parameter,
                            CultureInfo culture)
        {
            DateTime date = (DateTime)value;

            if (Evidences != null && Evidences.Contains(date))
                return new SolidColorBrush(Colors.Red);
            else if (date.Date == DateTime.Now.Date)
                return new SolidColorBrush(Colors.LightGray);
            else
                return new SolidColorBrush(Colors.Transparent);
        }

        public object ConvertBack(object value, Type targetType,
        object parameter, CultureInfo culture)
        {
            return null;
        }
    }
}


using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows.Data;

namespace ContabilitaSelfService.Converter
{
    public class TooltipDayCalendarConverter : IValueConverter
    {
        public static IDictionary<DateTime, string> Tooltips { get; set; }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            DateTime date = (DateTime)value;

            if (Tooltips != null && Tooltips.ContainsKey(date))
                return Tooltips[date];

            return null;
        }


        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    }
}

Ho creato poi in un’ottica di riusabilità il seguente user control
<UserControl x:Class="ContabilitaSelfService.UserControls.DeskTop"

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

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

    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

    xmlns:prim="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls"

    xmlns:src="clr-namespace:ContabilitaSelfService.Converter" xmlns:sdk="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" mc:Ignorable="d"

    d:DesignHeight="500" d:DesignWidth="180">

    <UserControl.Resources>

        <src:BackgroundDayCalendarConverter x:Key="BackgroundDayCalendarConverter" />

        <src:TooltipDayCalendarConverter x:Key="TooltipDayCalendarConverter" />

        <Style x:Key="CalendarDayButtonStyle1" TargetType="prim:CalendarDayButton">

            <Setter Property="Template">

                <Setter.Value>

                    <ControlTemplate TargetType="prim:CalendarDayButton">

                        <Grid Background="{Binding Converter={StaticResource BackgroundDayCalendarConverter}, Path=Date}"

                              ToolTipService.ToolTip="{Binding Converter={StaticResource TooltipDayCalendarConverter}, Path=Date}">

                            <ContentControl x:Name="Content" Margin="5,1,5,1" Content="{TemplateBinding Content}" />

                        </Grid>

                    </ControlTemplate>

                </Setter.Value>

            </Setter>

        </Style>

    </UserControl.Resources>

    <Grid x:Name="LayoutRoot" Background="White">
        <sdk:Calendar HorizontalAlignment="Right" CalendarDayButtonStyle="{StaticResource CalendarDayButtonStyle1}" >
        </sdk:Calendar>
    </Grid>
</UserControl>

Da notare la parte di binding relativa ai converter.
A questo punto ho caricato le collection interne Evidences e Tooltips prendendo le informazioni da una tabella, nella quale sono state memorizzate tutte le mie scadenze. Tralasciando la parte di recupero dei dati, questa è la procedura per il caricamento delle scadenze:

private void LoadScadenzeCallBack(LoadOperation<Scadenzario> obj)
        {
            BackgroundDayCalendarConverter.Evidences = new List<DateTime>();
            TooltipDayCalendarConverter.Tooltips = new Dictionary<DateTime, string>();

            foreach (var scadenza in _context.Scadenzarios)
            {
                BackgroundDayCalendarConverter.Evidences.Add(scadenza.DataScadenza);
                TooltipDayCalendarConverter.Tooltips.Add(scadenza.DataScadenza, scadenza.Descrizione);  
            }           
        }

Nel mio caso, questo è il risultato finale: