среда, 21 августа 2013 г.

Data binding WPF

   В данной статье я хотел бы рассказать о механизмах связывания данных, которые я использовал для отображения новых задач, поступающих в базу данных. 
  Об основах привязки данных в WPF можно прочитать по ссылкам в конце поста. Тут же я опишу только то, что мне понадобилось реальном проекте. Итак, исходные данные:
  • коллекция объектов, полученная из базы данных (List<Operations>)
  • TaskbarIcon – графический элемент в котором надо отобразить данные
      Для отображения моей коллекции я буду использовать UserControl, также можно использовать DataTemplate (об этом можно прочитать в по ссылке).
Сначала создаем объект <ResourceDictionary/> (подробнее про ресурсы) (словарь ресурсов), где и будет размещен наш TaskbarIcon.
Файл NotifyDictionary.xaml

<resourcedictionary 
     xmlns:local="clr-namespace:Wrapper" 
     xmlns:tb="http://www.hardcodet.net/taskbar"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <tb:taskbaricon iconsource="/Icons/Computers.ico"
     tooltiptext="Wrapper v.0.1"
     x:key="NotifIcon">
        <tb:taskbaricon .traypopup="">
            <local:usercontrol1>            
        </local:usercontrol1></tb:taskbaricon>
</tb:taskbaricon>
</resourcedictionary>
У объекта TaskbarIcon есть свойство TrayPopup, которое отображается при клике на иконку программы в трее. В это свойство мы и помещаем наш UserControl1. Далее нам надо подключить этот ResourceDictionary к приложению (файл App.xaml):

<application mc:ignorable="d" 
    startup="Application_Startup"
    x:class="Wrapper.App"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">   
    <application .resources="">
        <resourcedictionary>
            <resourcedictionary .mergeddictionaries="">
               "<resourcedictionary source="NotifyDictionary.xaml"></resourcedictionary>"
           </resourcedictionary>
        </resourcedictionary>
    </application>
</application>
Так как нам нужна дополнительная корректировка привязок в коде в App.xaml дописываем строчку с запускаемым методом (Startup="Application_Startup")
Файл App.xaml.cs:

private void Application_Startup(object sender, StartupEventArgs e)
        {
            var mainWindow = new MainWindow();
            Current.MainWindow = mainWindow;
            var viewModel = new MainViewModel(mainWindow);
            mainWindow.DataContext = viewModel;
            mainWindow.Tb = (TaskbarIcon)FindResource("NotifIcon");
            if (mainWindow.Tb != null) 
                mainWindow.Tb.DataContext = viewModel;
        }

Разберем поподробнее этот код:
MainViewModel имеет конструктор (MainViewModel(IView view)), который передает ссылку на view в private член типа IView MainViewModel-а. Это нужно для того, чтобы не нарушая паттерн MVVM обращаться к элементам MainWindow. MainWindow для этого должен, соответственно, реализовывать пользовательский интерфейс IView.
В строке var viewModel = new MainViewModel(mainWindow); мы как раз передаем ссылку на MainWindow в MainViewModel.  Теперь в классе вьюмодели мы можем реализовать команду (и привязать ее к любому визуальному компоненту):

public ICommand ShowBallon
        {
            get { return _showBallon ?? (_showBallon = new RelayCommand(ShowNewBallon)); }
        }
        public void ShowNewBallon()
        {
            _view.SetReadyState();
        } 

,которая будет запускать метод интерфейса IView.SetReadyState() уже MainWindow:

public void SetReadyState()
        {
            if (textBox1.Text != null) Tb.ShowBalloonTip(textBox1.Text,"master",BalloonIcon.Info);
        }

Тут public TaskbarIcon Tb { get; set; } свойство MainWindow, к которому напрямую из MainViewModel обратиться нельзя.
Далее задаем контекст данных MainWindow, это нужно чтобы свойства MainViewModel можно было привязывать к визуальным компонентам MainWindow:
mainWindow.DataContext = viewModel; Здесь свойство DataContext задается в коде, так как если это делать через XAML, то нужен был бы конструктор без параметров, например так: 


        
    

В последних строчках кода инициализируется объект TaskbarIcon (link) из файла ресурсов и ему присваивается контекст данных:

mainWindow.Tb = (TaskbarIcon)FindResource("NotifIcon");
            if (mainWindow.Tb != null) 
                mainWindow.Tb.DataContext = viewModel;


Теперь перейдем непосредственно к UserControl1:

<usercontrol
      d:designheight="311"
      d:designwidth="554"
      mc:ignorable="d"
      x:class="Wrapper.UserControl1" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <border borderthickness="5" cornerradius="10" height="232" opacity="1" width="255">
        <listbox height="223" itemssource="{Binding ItemsT, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" name="listBox1" width="245">
    </listbox></border>
</usercontrol>

Он состоит из элемента Border и ListBox. Так как DataContext юзерконтрола уже определен, ItemsSource у ListBox-a можно спокойно привязать к свойству ItemsT класса MainViewModel.

    Свойство ItemsT представляет собой ObservableCollection<TaskItem>, где TaskItem – usercontrol, созданный для отображения заданий в виде (image string) в StackPanel.
Вот его код:

    
    
        
        


Тут стоит обратить внимание что DataContext приязан сам на себя.
   Далее в MainViewModel нам необходимо создать метод заполнения коллекции TaskItem, так как в UserControl1 помещается только 5 элементов, заполнение коллекции необходимо закольцевать:

while (rezCtn > 0)
{
    var newItem = new TaskItem();
    newItem.SetTaskItem(true, rez[rezCtn - 1].NameEngine);
    ItemsT.Insert(0, newItem);
    if (ItemsT.Count >= 6)
      ItemsT.Remove(ItemsT.Last());
    rezCtn -= 1;
}
В результате получилось:



Полезные ссылки:
1. msdn.microsoft.com/ru-ru/magazine/cc163299.aspx
2.msdn.microsoft.com/ru-ru/magazine/cc700358.aspx
3. habrahabr.ru/post/41108
4.msdn.microsoft.com/ru-ru/library/ms750613
5.www.codeproject.com/Articles/36468/WPF-NotifyIcon

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

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

Следующее Предыдущее Главная страница