пятница, 27 августа 2010 г.

Использование Silverlight 4. Часть 9

Сейчас, для того, чтобы увидеть окошко с формой для заполнения данных, нам нужно нажимать на кнопку View. Это не очень реалистичный сценарий. Лучше открывать форму по двойному нажатию кнопки мыши.

Часть кода, который будет описан дальше, был найден мной в интеренете путем долгих поисков, возможно, кому-то это сэкономит время.

К сожалению, Silverlight не предосталяет никакой стандартной функциональности для этого. Придется разработывать ее самостоятельно.

Сначала сделаем грид нередактируемым.

<sdk:DataGrid AutoGenerateColumns="False" ItemsSource="{Binding ElementName=customerDomainDataSource, Path=Data}" Margin="0,123,0,0" Name="customerDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected" VerticalAlignment="Top" Width="400" IsReadOnly="True">

<sdk:DataGrid.Columns>

<sdk:DataGridTextColumn x:Name="nameColumn" Binding="{Binding Path=Name}" Header="Name" Width="SizeToHeader" />

<sdk:DataGridTextColumn x:Name="townIDColumn" Binding="{Binding Path=Town.Name}" Header="Town" Width="SizeToHeader" />

</sdk:DataGrid.Columns>

</sdk:DataGrid>

Теперь будем добавлять реакцию на двойное нажатие. Делать мы это будем через поведения, но сначала нужно создать класс, который будем обрабатывать события, полученные с мыши (да, даже этого Silverlight не будет делать сам).

using System;

using System.Net;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Documents;

using System.Windows.Ink;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Animation;

using System.Windows.Shapes;

using System.Threading;

namespace BusinessApplication1.Helpers

{

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)

{

(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

}

}

Код MouseClickManager не слишком сложный, но можно, в принципе, в нем и не разбираться, главное, что он работает.

Теперь нужно добавить поведение для грида. Для того, чтобы иметь возможность добавлять собственные поведения, нужно добавить в проект ссылку на System.Windows.Interactivity, который содержится в Blend SDK. Забавно, что Silverlight SDK его нет.

Теперь дабавим класс поведения:

using System;

using System.Net;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Documents;

using System.Windows.Ink;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Animation;

using System.Windows.Shapes;

using System.Windows.Interactivity;

namespace BusinessApplication1.Helpers

{

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);

}

}

}

Теперь «прицепим» это поведение к гриду

<UserControl

x:Class="BusinessApplication1.MainPage"

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

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

xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"

xmlns:uriMapper="clr-namespace:System.Windows.Navigation;assembly=System.Windows.Controls.Navigation"

xmlns:dataControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices" xmlns:my="clr-namespace:BusinessApplication1.Web.Services" xmlns:my1="clr-namespace:BusinessApplication.BLL"

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

xmlns:behaviors="clr-namespace:BusinessApplication1.Helpers;assembly=BusinessApplication1"

>

<Grid x:Name="LayoutRoot" Style="{StaticResource LayoutRootGridStyle}">

<Border x:Name="ContentBorder" Style="{StaticResource ContentBorderStyle}">

<navigation:Frame x:Name="ContentFrame" Style="{StaticResource ContentFrameStyle}"

Source="/Home" Navigated="ContentFrame_Navigated" NavigationFailed="ContentFrame_NavigationFailed">

<navigation:Frame.UriMapper>

<uriMapper:UriMapper>

<uriMapper:UriMapping Uri="" MappedUri="/Views/Home.xaml"/>

<uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}.xaml"/>

</uriMapper:UriMapper>

</navigation:Frame.UriMapper>

</navigation:Frame>

</Border>

<Grid Style="{StaticResource NavigationOuterGridStyle}">

<Grid x:Name="NavigationGrid" Style="{StaticResource NavigationGridStyle}">

<Border x:Name="BrandingBorder" Style="{StaticResource BrandingBorderStyle}">

<StackPanel x:Name="BrandingStackPanel" Style="{StaticResource BrandingStackPanelStyle}">

<ContentControl Style="{StaticResource LogoIcon}"/>

<TextBlock x:Name="ApplicationNameTextBlock" Style="{StaticResource ApplicationNameStyle}"

Text="{Binding ApplicationStrings.ApplicationName, Source={StaticResource ResourceWrapper}}"/>

</StackPanel>

</Border>

<Border x:Name="LinksBorder" Style="{StaticResource LinksBorderStyle}">

<StackPanel x:Name="LinksStackPanel" Style="{StaticResource LinksStackPanelStyle}">

<HyperlinkButton x:Name="Link1" Style="{StaticResource LinkStyle}"

NavigateUri="/Home" TargetName="ContentFrame" Content="{Binding Path=ApplicationStrings.HomePageTitle, Source={StaticResource ResourceWrapper}}"/>

<Rectangle x:Name="Divider1" Style="{StaticResource DividerStyle}"/>

<HyperlinkButton x:Name="Link2" Style="{StaticResource LinkStyle}"

NavigateUri="/About" TargetName="ContentFrame" Content="{Binding Path=ApplicationStrings.AboutPageTitle, Source={StaticResource ResourceWrapper}}"/>

</StackPanel>

</Border>

</Grid>

<Border x:Name="loginContainer" Style="{StaticResource LoginContainerStyle}">

<!-- LoginStatus will be added here in code behind. This is required for the designer view to work -->

</Border>

<StackPanel>

<sdk:DataGrid AutoGenerateColumns="False" ItemsSource="{Binding ElementName=customerDomainDataSource, Path=Data}" Margin="0,123,0,0" Name="customerDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected" VerticalAlignment="Top" Width="400" IsReadOnly="True">

<sdk:DataGrid.Columns>

<sdk:DataGridTextColumn x:Name="nameColumn" Binding="{Binding Path=Name}" Header="Name" Width="SizeToHeader" />

<sdk:DataGridTextColumn x:Name="townIDColumn" Binding="{Binding Path=Town.Name}" Header="Town" Width="SizeToHeader" />

</sdk:DataGrid.Columns>

<i:Interaction.Behaviors>

<behaviors:DataGridDoubleClickBehavior DoubleClick="customerDataGrid_DoubleClick"/>

</i:Interaction.Behaviors>

</sdk:DataGrid>

<Button Name="btnView" Content="View" Click="btnView_Click" Width="120"></Button>

<dataControls:DataForm CurrentItem="{Binding ElementName=customerDataGrid, Path=SelectedItem, Mode=TwoWay}"

AutoGenerateFields="True"

AutoCommit="False"

AutoEdit="False"

Width="400"

EditEnded="DataForm_EditEnded"

AutoGeneratingField="DataForm_AutoGeneratingField"

Name="customerDataForm"

CommandButtonsVisibility="All"

ItemsSource="{Binding Path=Data, ElementName=customerDomainDataSource}"

DeletingItem="customerDataForm_DeletingItem">

</dataControls:DataForm>

</StackPanel>

</Grid>

<riaControls:DomainDataSource AutoLoad="True" d:DesignData="{d:DesignInstance my1:Customer, CreateList=true}" Height="0" LoadedData="customerDomainDataSource_LoadedData" Name="customerDomainDataSource" QueryName="GetCustomersQuery" Width="0">

<riaControls:DomainDataSource.DomainContext>

<my:DomainService1 />

</riaControls:DomainDataSource.DomainContext>

</riaControls:DomainDataSource>

</Grid>

</UserControl>

Ну и обработчик двойного клика аналогичен обработчику нажатия кнопки View:

private void customerDataGrid_DoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)

{

CustomerView view = new CustomerView(customerDataGrid.SelectedItem as Customer, customerDomainDataSource);

view.Show();

}

Комментариев нет:

Отправить комментарий