Реализация шаблонов мастер страниц в Silverlight

ОГЛАВЛЕНИЕ

Есть несколько статей о том, как реализовать шаблон мастер страницы в Silverlight. Вопрос в том, действительно ли нам нужна возможность создавать шаблон мастер страницы в Silverlight. Если есть преимущества использования функции шаблона мастер страницы в ASP.NET, то нет причины, из-за которой Silverlight не мог бы использовать это преимущество

Введение

Есть несколько статей о том, как реализовать шаблон мастер страницы в Silverlight. Вопрос в том, действительно ли нам нужна возможность создавать шаблон страницы в Silverlight. Если есть преимущества использования функции шаблона страницы в ASP.NET, то нет причины, из-за которой Silverlight не мог бы использовать это преимущество. Эта статья показывает, как создать приложение со стандартным стилем оформления шаблона мастер страницы в Silverlight.

Системные требования

Конструктивные требования

Как и у традиционных веб-страниц, страница входа в систему при запуске выглядит как на Рис 1. Окно входа в систему состоит из 2 текстовых полей, 1 поля со списком и 2 кнопок. Текстовые поля используются для ввода пользователем имени пользователя и пароля, поле со списком используется для выбора рабочей среды при входе. Кнопка Cancel (отменить) удаляет все данные в текстовых полях и кнопка Login (войти) используется для отправки информации для аутентификации.


Рис 1. Страница входа в систему

После нажатия кнопки входа главная страница отобразит то же, что и на Рис 2.


Рис 2. Главная страница

Главная страница имеет два основных раздела: раздел шаблона мастер страницы и раздел подстраницы, как на Рис 3. Раздел шаблона мастер страницы имеет панель управляющих кнопок сверху и древовидное меню с левой стороны. Раздел подстраницы включает в себя область содержимого (контента) справа.


Рис 3. Раздел шаблона мастер страницы и раздел подстраницы

Шаблон мастер страницы состоит из:

  • Метка названия формы: для отображения идентификатора подформы
  • Метка идентификатора пользователя: для отображения текущего пользователя
  • Метка системы: для отображения названия рабочей среды системы
  • Метка даты: для отображения текущей даты
  • Метка количества: для отображения числа информационных записей
  • Метка состояния: для отображения текущего состояния
  • Древовидное меню: для динамического изменения содержимого в области содержимого
  • 11 управляющих кнопок: для выполнения действий над подстраницей

Рис 4. Элементы шаблона мастер страницы и подстраницы

11 кнопок управления включают следующие:

  • Поиск: для запуска состояния поиска
  • Выполнить: для извлечения данных с сервера и передачи их клиенту, а также для запуска состояния изменения
  • Редактировать: для выполнения редактирования полей
  • Удалить: удалить текущую запись
  • Сохранить: сохранить изменения при обновлении
  • Первая запись: перейти к первой записи
  • Предыдущая запись: перейти к предыдущей записи
  • Следующая запись: перейти к следующей записи
  • Последняя запись: перейти к последней записи
  • Excel: экспортировать данные в excel
  • Выйти: выйти и закрыть браузер

Рис 5. Описание 11 кнопок управления

Есть 4 вида состояний:

Начальное: Активна кнопка поиска, как на Рис 6.


Рис 6. Начальное состояние

Поиск: Поиск и Выполнить активны, как на Рис 7.


Рис 7. Состояние поиска

Изменить: Все кнопки активны, исключая кнопку выполнения, как на Рис 8.


Рис 8. Состояние изменения

Клиентское: Вы можете решить, какие кнопки сделать активными/неактивными, например, вы можете сделать активными все кнопки, как на Рис 9.


Рис 9. Клиентское состояние

Древовидное меню можно раскрыть или свернуть, как на Рис 10.


Рис 10. Раскрытие древовидного меню


Краткое описание каждого проекта

Имеются 4 проекта:

  • DataObjectCollection : Структуры данных, используемые сервисом для соединения с клиентом
  • CommandInMasterDaoWcf: Сервис, передающий данные от сервера к клиенту.
  • CommandInMasterDemo : Проект приложения Silverlight.

Есть 3 основных элемента управления Silverlight, к которым нужно обратиться:

·         LeftTreeViewMain – Древовидное меню

·         Login Control – Страница входа в систему

·         TopToolBar – Элемент управления, содержащий 11 управляющих кнопок

  • CommandInMasterDemo.Web: Создается, когда вы создаете приложение Silverlight. Оно будет содержать управляющие элементы Silverlight, чтобы запускать их в браузере.

Рис 11. Проекты

Использование кода

Перед тем как мы начнем изучать пример, нужно описать несколько методов.

App.xaml.cs

Для классификации 4 состояний: начальное, поиск, изменение и клиентское

public enum WorkState
{

            INT,//Начальное состояние
        SEA,//Состояние поиска
        MOD,//Состояние изменения
        CUS,//Клиентское состояние
}

Получить или установить древовидное меню

public static System.Collections.ObjectModel.Collection<MenuDataContext> MenuList { get; set; } 

Получить или установить идентификатор текущей формы

public static string CurrentFormID 
{
    get;
    set;
}

В методе Application_Startup добавляется окно входа в систему RootVisual. Он будет создавать окно входа всистему при запуске.

private void Application_Startup(object sender, StartupEventArgs e)
{
    this.RootVisual = rootVisual;
    rootVisual.Children.Add(lg);
}

Мы используем отражение для создания экземпляра пользовательского элемента управления. Например, MainPage имеет пространство имен CommandInMasterDemo и имя класса MainPage, поэтому можно использовать отражение для создания объекта CommandInMasterDemo.MainPage, и затем преобразовать его в тип UserControl. Если пользовательский элемент управления не равен нулю, мы записываем в CurrentFormID strName, и затем добавляем пользовательский элемент управления в текущий Application.RootVisual.

public static void Navigate(string strName)
{
    App CurrentApp = (App)Application.Current;
    Type type = CurrentApp.GetType();
    Assembly assembly = type.Assembly;
    UserControl uc = (UserControl)assembly.CreateInstance(type.Namespace + "." + strName);
    if (uc != null)
    {
        CurrentFormID = strName;
        CurrentApp.rootVisual.Children.Clear();
        CurrentApp.rootVisual.Children.Add((UserControl)assembly.CreateInstance(type.Namespace + "." + strName));

    }
}

GetUserControl также использует отражение для создания экземпляра пользовательского элемента управления. Единственное различие между Navigate и GetUserControl – подготовка пространства имен. Все подстраницы имеют свою собственную папку подгруппы, такую как CHM, FCM. Поэтому нам нужно добавить имя подгруппы в пространство имен. Например, пользовательский элемент управления FCM201 имеет пространство имен CommandInMasterDemo.FCM и имя класса FCM201, поэтому мы используем type.Namespace + "." + strName.Substring(0, 3) + "." + strName, чтобы создать его экземпляр.

public static UserControl GetUserControl(string strName) 
{
    CurrentFormID = strName;
    App CurrentApp = (App)Application.Current;
    Type type = CurrentApp.GetType();
    Assembly assembly = type.Assembly;
    return (UserControl)assembly.CreateInstance(type.Namespace + "." + strName.Substring(0, 3) + "." + strName);
}

LoginControl.xaml.cs

Кнопка входа в систему запустит proxy_GetUserInfoCompleted, затем proxy_GetUserInfoCompleted запуститproxy_GetFunctionMenuCompleted

private void btnLogin_Click(object sender, RoutedEventArgs e)
{
    if (string.IsNullOrEmpty(txtAccount.Text) || string.IsNullOrEmpty(txtPassword.Password))
    {
        txtErrorInformation.Text = "Account and Password must enter";
        return;
    }
    else
    {
        this.Cursor = Cursors.Wait;
        try
        {
            DaoWcfClient daoWcf = new DaoWcfClient();
            daoWcf.GetUserInfoCompleted += new EventHandler<GetUserInfoCompletedEventArgs>(proxy_GetUserInfoCompleted);
            daoWcf.GetUserInfoAsync(txtAccount.Text, txtPassword.Password);
        }
        catch (Exception ex)
        {
            MessageBox.Show("btnLogin_Click Exception: " + ex.Message);
        }
        finally
        {
            this.Cursor = Cursors.Arrow;
        }
    }
}

void proxy_GetUserInfoCompleted(object sender, GetUserInfoCompletedEventArgs e)
{
    try
    {
        strCurrentUser = e.Result;
        if (string.IsNullOrEmpty(strCurrentUser))
        {
            txtErrorInformation.Text = "Account or Password is incorrect";
        }
        else
        {
            Resources.Remove("CurrentUser");
                  Resources.Add("CurrentUser", txtAccount.Text);
                       Resources.Remove("CurrentDatabase");
            Resources.Add("CurrentDatabase", cbDb.SelectionBoxItem.ToString());
            DaoWcfClient daoWcf = new DaoWcfClient();
            daoWcf.GetFunctionMenuCompleted += new EventHandler<GetFunctionMenuCompletedEventArgs>(proxy_GetFunctionMenuCompleted);
            daoWcf.GetFunctionMenuAsync(txtAccount.Text);
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("proxy_FindUserInfoCompleted Exception: " + ex.Message);
    }
}
void proxy_GetFunctionMenuCompleted(object sender, GetFunctionMenuCompletedEventArgs e)
{
    System.Collections.ObjectModel.Collection<MenuDataContext> list = e.Result;
    if (list.Count > 0)
    {
        App CurrentApp = (App)Application.Current;
        App.MenuList = list;
        App.Navigate("MainPage");
    }
}

Давайте изучим proxy_GetUserInfoCompleted. Мы сохраняем идентификатор пользователя и имя системы, используя Ресурсы.

Resources.Remove("CurrentUser");
Resources.Add("CurrentUser", txtAccount.Text);
Resources.Remove("CurrentDatabase");
Resources.Add("CurrentDatabase", cbDb.SelectionBoxItem.ToString());

В proxy_GetFunctionMenuCompleted после получения списка меню мы сохраняем список меню в свойстве App MenuList, затем используем App.Navigate, чтобы перейти на Главную страницу.

System.Collections.ObjectModel.Collection<MenuDataContext> list = e.Result;
if (list.Count > 0)
{
    App CurrentApp = (App)Application.Current;
    App.MenuList = list;
    App.Navigate("MainPage");
}

Мы создали делегат MenuEventHandler(object sender, RouteEventArgs e) для всех кнопок управления.

public delegate void MenuEventHandler(object sender, RoutedEventArgs e); 

Каждая кнопка управления имеет свое собственное событие

public event MenuEventHandler SearchClick; 
public event MenuEventHandler ExecuteClick;
public event MenuEventHandler EditClick;
public event MenuEventHandler DeleteClick;
public event MenuEventHandler SaveClick;
public event MenuEventHandler LastClick;
public event MenuEventHandler FirstClick;
public event MenuEventHandler PreviousClick;
public event MenuEventHandler NextClick;
public event MenuEventHandler ExcelClick;

Имеется 3 свойства:

  • Текущее состояние : Получить/установить текущие состояния
public WorkState CurrentState
{
    get
         {
                return curretState;
         }
         set
    {
                curretState = value;
                SetButtonState();
         }
}
  • BindGrid : Получить/Установить элемент управления DataGrid (сетка данных), чтобы он мог взаимодействовать с кнопками Первая, Предыдущая, Следующая и Последняя.
public DataGrid BindGrid { get; set; }
  • TotalRowCount (общее число строк) : Получить/Установить общее число записей данных
public int TotalRowCount { get; set; }

SetButtonState (установить состояние кнопки) используется, чтобы скрыть/показать кнопки управления в различных состояниях.

private void SetButtonState()
{
    switch (CurrentState)
    {
        case WorkState.INT:
        txtStatus.Text = "Initial";
        //Поиск
        btnSearch.IsEnabled = true;
        imgbtnSearchOn.Visibility = Visibility.Visible;
        ...............
        break;
        case WorkState.SEA:
        txtStatus.Text = "Search";
        //Поиск
        btnSearch.IsEnabled = true;
        imgbtnSearchOn.Visibility = Visibility.Visible;
        ...............
        break;
        case WorkState.MOD:
        txtStatus.Text = "Modify";
        //Поиск
        btnSearch.IsEnabled = true;
        imgbtnSearchOn.Visibility = Visibility.Visible;
        ...............
        break;
        case WorkState.CUS:
        txtStatus.Text = "Custom";
        break;
        default:
        txtStatus.Text = "Search";
        //Поиск
        btnSearch.IsEnabled = true;
        imgbtnSearchOn.Visibility = Visibility.Visible;
        ...............
        break;
    }
}

Есть два способа включить 4 кнопки перемещения записей (Первая, Предыдущая, Следующая и Последняя). Одна для взаимодействия с элементом управления DataGrid, другая для его запуска в подстранице.

private void btnLast_Click(object sender, RoutedEventArgs e)
{
    if (BindGrid != null)
    {
        BindGrid.SelectedIndex = TotalRowCount - 1;
    }
    else
    {
        LastClick(this, e);
    }
}
private void btnNext_Click(object sender, RoutedEventArgs e)
{
    if (BindGrid != null)
    {
        if (BindGrid.SelectedIndex != TotalRowCount - 1)
        {
            BindGrid.SelectedIndex = BindGrid.SelectedIndex + 1;
        }
    }
    else
    {
        NextClick(this, e);
    }
}
private void btnPrevious_Click(object sender, RoutedEventArgs e)
{
    if (BindGrid != null)
    {
        if (BindGrid.SelectedIndex != 0)
        {
            BindGrid.SelectedIndex = BindGrid.SelectedIndex - 1;
        }
    }
    else
    {
        PreviousClick(this, e);
    }
}
private void btnFirst_Click(object sender, RoutedEventArgs e)
{
    if (BindGrid != null)
    {
        BindGrid.SelectedIndex = 0;
    }
    else
    {
        FirstClick(this, e);
    }
}

CommonUtility.cs
Чтобы получить элемент управления TopToolBar, сначала нужно найти элемент управления MainPage, потому что MainPage содержит TopToolBar.

public MainPage GetMainPage(UserControl currentPage, bool blSub)
{
    MainPage mainPage = blSub ? (MainPage)((Grid)((Grid)((Grid)currentPage.Parent).Parent).Parent).Parent : (MainPage)((Grid)((Grid)currentPage.Parent).Parent).Parent;
    return mainPage;
}

После того как мы нашли элемент управления MainPage, можно использовать метод FindName (найти имя),чтобы получить элемент управления TopToolBar.

public  GetTopToolBar(UserControl currentPage, bool blSub)
{
     ttb = GetMainPage(currentPage, blSub).FindName("topToolBar") as ;
    return ttb;
}

ExportExcel.ashx.cs

The Silverlight не имеет возможности сохранить файл на локальный диск, поэтому используется обработчик для создания файла CSV/Excel.

public void ProcessRequest(HttpContext context)
{
    string strContext = context.Request.QueryString["Context"] != null ?
    HttpUtility.UrlDecode(context.Request.QueryString["Context"]) : DateTime.Now.ToString("yyyyMMdd_HHmmss");
    string[] strSplit = strContext.Replace("[", "").Replace("]", "").Split(char.Parse(";"));
    string strFileName = strSplit[0];
    string strQueryCase = strSplit[1];
    DataGrid dg = new DataGrid();
    switch (strQueryCase)
    {
        case "FindMTAccntScopeByYear":
        List<AccountDataContext> list = new ().GetAccountByYear(strSplit[2]);
             dg.DataSource = list;
        break;
        case "FindAllyCompAccountByOwnerId":
        List<AllyCompAcctDataContext> listAlly = new DaoWcf().GetAllyCompAccountByOwnerId(strSplit[2]);
              dg.DataSource = listAlly;
        break;
    }
    dg.DataBind();
    context.Response.Buffer = true;
    context.Response.ClearContent();
    context.Response.ClearHeaders();
    context.Response.ContentType = "application/vnd.ms-excel";
    context.Response.AddHeader("content-disposition", "attachment;filename=" + strFileName + ".xls");
    dg.HeaderStyle.ForeColor = Color.Blue;
    dg.HeaderStyle.BackColor = Color.White;
    dg.ItemStyle.BackColor = Color.White;
    System.IO.StringWriter tw = new StringWriter();
    System.Web.UI.HtmlTextWriter hw = new HtmlTextWriter(tw);
    dg.RenderControl(hw);
    context.Response.Write(tw.ToString());
    context.Response.Flush();
    context.Response.Close();
    context.Response.End();
}


Демонстрация

Есть 3 примера, которые мы собираемся разобрать:

Аппаратное обеспечение FCM201
Этот пример показывает, как использовать кнопку для запуска различных состояний.

private void Button_Click(object sender, RoutedEventArgs e) 
{
    Button b = (Button)sender;
    switch (b.Tag.ToString())
    {
        case "INT":
WorkState.INT;
        break;
        case "SEA":
WorkState.SEA;
        break;
        case "MOD":
WorkState.MOD;
        break;
        case "CUS":
WorkState.CUS;
true;
                        topToolBar.ExecuteEnable = true;
                        topToolBar.EditEnable = true;
                        topToolBar.DeleteEnable = true;
                        topToolBar.SaveEnable = true;
                        topToolBar.RecordMoveEnable = true;
                        topToolBar.ExcelEnable = true;
                  break;
    }
}

Начальная кнопка: для запуска начального состояния, как на Рис 12


Рис 12. Начальное состояние

Кнопка поиска: для запуска состояния поиска


Рис 13. Состояние поиска

Кнопка изменения: для запуска состояния изменения


Рис 14. Состояние изменения

Клиентская кнопка – для запуска клиентского состояния

Рис 15. Клиентское состояние

Программное обеспечение FCM202

Это порядок выполнения действий по умолчанию в общем случае. Порядок смены состояний таков – Начальное состояние -->Состояние поиска  --> Состояние изменения.

В начальном состоянии активна только кнопка Поиск.


Рис 16. Начальное состояние

После нажатия кнопки поиска состояние изменится на Поиск и станет видимой кнопка Выполнение. 


Рис 17. Состояние поиска

После нажатия на кнопку Выполнение станут видимы все управляющие кнопки, за исключением кнопки Выполнение. Теперь вы должны видеть данные, отображенные на странице содержимого.

Рис 18. Состояние изменения

При нажатии кнопки Удалить появится предупреждающее сообщение. Удаление не работает в этом примере.

Рис 19. Кнопка удаления

Нажатие кнопки редактирования сделает доступной возможность изменения редактируемых полей. Файл типа данных отобразит элемент управления календарь. Поле с множественным выбором отобразит комбинированный список.

Рис 20. Кнопка редактирования

После изменения данных вы можете нажать кнопку Сохранить, чтобы обновить данные на сервере.

Рис 21. Кнопка Сохранить

В FCM202 мы нажимаем кнопку перемещения по записям в элементе управления подстраницы.

voidobject sender, RoutedEventArgs e)
{
            iCurrent = 0;
            SetCountStatus(iCurrent);
}
voidobject sender, RoutedEventArgs e)
{
            iCurrent = list.Count - 1;
            SetCountStatus(iCurrent);
}
voidobject sender, RoutedEventArgs e)
{
         if (iCurrent != list.Count - 1)
    {
        iCurrent = iCurrent + 1;
        SetCountStatus(iCurrent);
    }
}
voidobject sender, RoutedEventArgs e)
{
    if (iCurrent != 0)
    {
        iCurrent = iCurrent - 1;
        SetCountStatus(iCurrent);
    }
}

Рис 22. Кнопка перемещения по записям

Передаем информацию обработчику, чтобы создать файл excel

voidobject sender, RoutedEventArgs e)
{
    string strOwnerId = txtOwnerId.Text;
         string strEncodeUrl = System.Windows.Browser.HttpUtility.UrlEncode("[AllyCompAcct;FindAllyCompAccountByOwnerId;" + strOwnerId + "]");
         string strUri = "http://localhost/CommandInMasterDaoWcf/ExportExcel.ashx?Context=" + strEncodeUrl;
    HtmlPage.Window.Navigate(new Uri(strUri, UriKind.Absolute));
}

Рис 23. Экспорт данных в excel

FCM203 Локальный

Это пользовательская последовательность состояний. Последовательность состояний такова: Начальное состояние  --> Состояние поиска  --> Клиентское состояние.
Чтобы сделать активным клиентское состояние, вам нужно установить topToolBar.CurrentState = WorkState.CUS


Рис 24. Клиентское состояние 

В FCM203 мы нажимаем кнопку перемещения по записям в TopToolBar, устанавливая сетку данных в свойстве BindGrid (привязать сетку) TopToolBar.

topToolBar.BindGrid = this.dgAccountYear;
topToolBar.TotalRowCount = list.Count;

Рис 25. Кнопка перемещения по записям

Двигаемся вперед

Мы сделали этот код общедоступным без ограничений. Он не является лучшим образцом проектирования или стиля написания кода. Любой может использовать его для любых целей, включая и создание коммерческих продуктов. Если вы можете улучшить код или сделать его более понятным, сообщите нам. Мы обновим код, чтобы сделать его более полезным.

 Загрузить CommandInMasterDemo.zip - 2.14 MB