Получил задание - реализовать групповое присвоение свойств, организационным структурам (департаментам, отделам). Немного подумав, над тем как, это будет выглядеть, я пришел к выводу, что лучше всего сделать дерево, в котором видно, само подразделение и его статус. Статусы я решил подсвечивать квадратиком (закрашенная плитка). "Погуглив", подходящих контролов типа 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