Получил задание - реализовать групповое присвоение свойств, организационным структурам (департаментам, отделам). Немного подумав, над тем как, это будет выглядеть, я пришел к выводу, что лучше всего сделать дерево, в котором видно, само подразделение и его статус. Статусы я решил подсвечивать квадратиком (закрашенная плитка). "Погуглив", подходящих контролов типа TreeView для ASP.NET MVC 3, которые бы меня устраивали, я не нашел. Решил все реализовывать на Silverlight.
ASP.NET MVC (часть)
Модели данных:
public class StructureTreeViewItem { public int ID { get; set; } public int? ParentID { get; set; } public string Name { get; set; } public int TypeID { get; set; } }
public class StructureType { public int ID { get; set; } public string Name { get; set; } }Контроллер
public JsonResult GetNodes() { using (var db = new ...Entities()) { var s = (... where .... select new StructureTreeViewItem() { ID = structs.StructureID, ParentID = structs.StructureParentID, Name = structsLangs.StructureName, TypeID = structs.StructureTypeId }).ToList(); return Json(s, JsonRequestBehavior.AllowGet); } } public JsonResult GetStructureTypes() { using (var db = new ...Entities()) { var structureTypesList = new List<structuretype>(); db.StructureTypes .OrderBy(q => q.id) .ToList() .ForEach(q => structureTypesList .Add(new StructureType() { ID = q.id, Name = q.Name })); return Json(structureTypesList, JsonRequestBehavior.AllowGet); } } public EmptyResult UpdateStructures(string structures) { if (!string.IsNullOrWhiteSpace(structures)) { using (var db = new ...Entities()) { var treeViewStructures = new List<structuretreeviewitem>(); structures.Split(';').ToList().ForEach(s => { var treeViewStructureDict = s.Split('='); int structureId, structureTypeId; if (treeViewStructureDict.Length == 2 && int.TryParse(treeViewStructureDict[0], out structureId) && int.TryParse(treeViewStructureDict[1], out structureTypeId)) { var dbSctructure = db.Structure.FirstOrDefault(q => q.StructureID == structureId); if (dbSctructure != null && db.StructureTypes.FirstOrDefault(q => q.id == structureTypeId) != null) { dbSctructure.StructureTypeId = structureTypeId; db.SaveChanges(); } } }); } } return new EmptyResult(); }
Silverlight (часть)
Модели
public class StructureType : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged<t>(Expression<func<t>> raiser) { var e = PropertyChanged; if (e != null) { var propName = ((MemberExpression)raiser.Body).Member.Name; e(this, new PropertyChangedEventArgs(propName)); } } private int m_id; public int ID { get { return m_id; } set { m_id = value; RaisePropertyChanged(() => this.ID); } } private string m_name; public string Name { get { return m_name; } set { m_name = value; RaisePropertyChanged(() => this.Name); } } private SolidColorBrush m_color; public SolidColorBrush Color { get { return m_color; } set { m_color = value; RaisePropertyChanged(() => this.Color); } } }
public class StructureItem : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged<t>(Expression<func<t>> raiser) { var e = PropertyChanged; if (e != null) { var propName = ((MemberExpression)raiser.Body).Member.Name; e(this, new PropertyChangedEventArgs(propName)); } } public StructureItem() { m_checked = false; m_childItems = new ObservableCollection<structureitem>(); } private int m_id; public int ID { get { return m_id; } set { m_id = value; RaisePropertyChanged(() => this.ID); } } private int? m_parentId; public int? ParentID { get { return m_parentId; } set { m_parentId = value; RaisePropertyChanged(() => this.ParentID); } } private string m_name; public string Name { get { return m_name; } set { m_name = value; RaisePropertyChanged(() => this.Name); } } private bool m_checked; public bool Checked { get { return m_checked; } set { m_checked = value; RaisePropertyChanged(() => this.Checked); } } private StructureType m_structureType; public StructureType StructureType { get { return m_structureType; } set { m_structureType = value; RaisePropertyChanged(() => this.StructureType); } } ObservableCollection<structureitem> m_childItems; public ObservableCollection<structureitem> ChildItems { get { return m_childItems; } set { m_childItems = value; RaisePropertyChanged(() => this.ChildItems); } } }При загрузке Silverlight приложение, подтягивает данные, о типах структурных подразделений. После чего, они привязываются к ComboBox-у, а затем загружаются сами структурные подразделения. После загрузки структурных подразделений, происходит их сортировка в иерархическую коллецию с помощью рекурсии.
public MainPage() { InitializeComponent(); onEndLoad(); } private void disableUi() { Dispatcher.BeginInvoke(() => { // Бизи индикатор bi.IsBusy = true; }); } private void enableUi() { Dispatcher.BeginInvoke(() => { // Отключение бизи индикатора bi.IsBusy = false; }); } private void onEndLoad() { disableUi(); var webClient = new WebClient(); webClient.OpenReadCompleted += onGetStructureTypesCompleted; webClient.OpenReadAsync(new Uri("/Core/GetStructureTypes", UriKind.Relative)); } private void onGetStructureTypesCompleted(object sender, OpenReadCompletedEventArgs e) { if (e.Error != null) { if (e.Error.Message != null) { MessageBox.Show(e.Error.Message); } enableUi(); return; } var items = (JsonArray)JsonValue.Load(e.Result); var typedItems = new ObservableCollection<structuretype>(); foreach (var item in items) { var structureType = new StructureType(); structureType.ID = int.Parse(item["ID"].ToString()); structureType.Name = item["Name"].ToString().Replace("\\\"", "\"").Trim('"').Trim(); // Тут подбираем цвета для каждого из типов структур switch (structureType.Name) { case "Сервисное": structureType.Color = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0x72, 0x72)); break; case "Зарабатывающее": structureType.Color = new SolidColorBrush(Color.FromArgb(0xFF, 0x40, 0xC7, 0x40)); break; default: structureType.Color = new SolidColorBrush(Color.FromArgb(0xFF, 0xB4, 0xB4, 0xB4)); break; } typedItems.Add(structureType); } cbStructureTypes.ItemsSource = typedItems; var serviceUri = new Uri("/Core/GetNodes", UriKind.Relative); var webClient = new WebClient(); webClient.OpenReadCompleted += onGetNodesCompleted; webClient.OpenReadAsync(serviceUri); } private void onGetNodesCompleted(object sender, OpenReadCompletedEventArgs e) { if (e.Error != null) { if (e.Error.Message != null) { MessageBox.Show(e.Error.Message); } enableUi(); return; } var notSortedItems = new ObservableCollection<structureitem>(); var structureTypes = cbStructureTypes.ItemsSource as ObservableCollection<structuretype>; var nodes = (JsonArray)JsonValue.Load(e.Result); foreach (var node in nodes) { var structureItem = new StructureItem(); structureItem.ID = int.Parse(node["ID"].ToString()); structureItem.Name = node["Name"].ToString().Replace("\\\"", "\"").Replace("\"\"", "\""); var typeId = int.Parse(node["TypeID"].ToString()); foreach (var sType in structureTypes) { if (sType.ID == typeId) { structureItem.StructureType = sType; } } structureItem.ParentID = node["ParentID"] == null || string.IsNullOrWhiteSpace(node["ParentID"].ToString()) ? 0 : int.Parse(node["ParentID"].ToString()); notSortedItems.Add(structureItem); } var sortedItems = new ObservableCollection<structureitem>(); notSortedItems.ToList().ForEach(q => { if (q.ParentID.HasValue && q.ParentID.Value == 0) { sortedItems.Add(storeTree(q, notSortedItems)); } }); twStructures.ItemsSource = sortedItems; enableUi(); } private StructureItem storeTree(StructureItem item, ObservableCollection<structureitem> allItems) { var childItems = allItems.Where(q => q.ParentID == item.ID).ToList(); if (childItems.Count > 0) { childItems.ForEach(q => { item.ChildItems.Add(storeTree(q, allItems)); }); } return item; } // Обработчик события нажатия на чекбокс в дереве private void checkBox_Click(object sender, RoutedEventArgs e) { if (m_selectedItems == null) m_selectedItems = new List<structureitem>(); var currentCheckBox = sender as CheckBox; if (currentCheckBox.IsChecked.HasValue && currentCheckBox.IsChecked.Value) { m_selectedItems.Add(currentCheckBox.Tag as StructureItem); } else { m_selectedItems.Remove(currentCheckBox.Tag as StructureItem); } } private void bSave_Click(object sender, RoutedEventArgs e) { if (cbStructureTypes.SelectedItem != null && m_selectedItems != null && m_selectedItems.Count > 0) { disableUi(); m_selectedItems.ForEach(q => { q.StructureType = cbStructureTypes.SelectedItem as StructureType; q.Checked = false; }); sendToSite(); m_selectedItems.Clear(); enableUi(); } } // Вызов метода апдейт private void sendToSite() { var webClient = new WebClient(); webClient.Headers["Content-type"] = "application/x-www-form-urlencoded"; var parametersString = new StringBuilder(); m_selectedItems.ForEach(s => { if (parametersString.ToString() != string.Empty) parametersString.Append(";"); parametersString.AppendFormat("{0}={1}", s.ID, s.StructureType.ID); }); webClient.UploadStringAsync(new Uri("/Core/UpdateStructures/", UriKind.Relative), "POST", string.Format("structures={0}", parametersString.ToString())); }Разметка XAML
<Grid.Resources> <sdk:HierarchicalDataTemplate x:Key="tvTemplate" ItemsSource="{Binding Path=ChildItems}" > <StackPanel Orientation="Horizontal"> <CheckBox Tag="{Binding}" IsChecked="{Binding Path=Checked, Mode=TwoWay}" Click="checkBox_Click" /> <Rectangle Height="15" Width="15" Margin="0,0,3,0" StrokeThickness="1" Fill="{Binding Path=StructureType.Color}"/> <TextBlock Text="{Binding Path=Name}" /> </StackPanel> </sdk:HierarchicalDataTemplate> </Grid.Resources> <sdk:TreeView Margin="12,41,12,12" Name="twStructures" BorderBrush="#FFB1B1B1" ItemsSource="{Binding}" ItemTemplate="{StaticResource tvTemplate}" SelectedValuePath="ID"/> <ComboBox Height="23" Margin="12,12,0,0" Name="cbStructureTypes" VerticalAlignment="Top" ItemsSource="{Binding}" HorizontalAlignment="Left" Width="219"> <ComboBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <Rectangle Height="15" Width="15" Margin="0,0,3,0" StrokeThickness="1" Fill="{Binding Path=Color}"/> <TextBlock Text="{Binding Path=Name}" /> </StackPanel> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox> <Button Content="Сохранить" Height="23" Margin="237,12,0,0" Name="bSave" VerticalAlignment="Top" HorizontalAlignment="Left" Width="75" Click="bSave_Click" /> <toolkit:BusyIndicator BusyContent="Loading please wait..." Name="bi" />
Интересно, кто нибудь, умеет правильно вызывать методы контроллеров с параметрами из Silverlight?
Статусы в фильтре в первой картинке смешные.. Спасибо, развеселил!
ReplyDelete