private async Task AskIfSave(bool cancel) { if (baseViewModel.IsModified) { var result = await messageBoxService.ShowDialog(new MessageBoxFactory <int>() .SetTitle("Save changes") .SetMainInstruction($"{Title} has unsaved changes") .SetContent("Do you want to save them before picking the row?") .WithNoButton(1) .WithYesButton(2) .WithCancelButton(0) .Build()); if (result == 0) { return; } if (result == 2) { ExecuteChangedCommand.Execute(null); } } if (cancel) { CloseCancel?.Invoke(); } else { CloseOk?.Invoke(); } }
public NewItemDialogViewModel(ISolutionItemProvideService provider) { Dictionary <string, NewItemPrototypeGroup> groups = new(); ItemPrototypes = new ObservableCollection <NewItemPrototypeGroup>(); foreach (var item in provider.AllCompatible) { if (!groups.TryGetValue(item.GetGroupName(), out var group)) { group = new NewItemPrototypeGroup(item.GetGroupName()); groups[item.GetGroupName()] = group; ItemPrototypes.Add(group); } var info = new NewItemPrototypeInfo(item); group.Add(info); FlatItemPrototypes.Add(info); } Accept = new DelegateCommand(() => { CloseOk?.Invoke(); }); Cancel = new DelegateCommand(() => { CloseCancel?.Invoke(); }); if (ItemPrototypes.Count > 0 && ItemPrototypes[0].Count > 0) { SelectedPrototype = ItemPrototypes[0][0]; } }
public void OnClose() { if (baseViewModel.IsModified) { AskIfSave(true).ListenErrors(); } else { CloseCancel?.Invoke(); } }
public GenericSelectorDialogViewModel(IEnumerable <ColumnDescriptor> columns, IEnumerable <T> collection, Func <T, int> entryGetter, Func <T, string> index) { this.entryGetter = entryGetter; this.index = index; items = new SourceList <T>(); ReadOnlyObservableCollection <T> l; var currentFilter = this.WhenValueChanged(t => t.SearchText).Select <string?, Func <T, bool> >(val => { if (string.IsNullOrEmpty(val)) { return(model => true); } var lowerCase = val.ToLower(); return(model => index(model).ToLower().Contains(lowerCase)); }); items .Connect() .Filter(currentFilter, ListFilterPolicy.ClearAndReplace) .Sort(Comparer <T> .Create((x, y) => entryGetter(x).CompareTo(entryGetter(y)))) .Bind(out l) .Subscribe(); FilteredItems = l; Columns = new ObservableCollection <ColumnDescriptor>(); foreach (var column in columns) { Columns.Add(column); } items.AddRange(collection); Accept = new DelegateCommand(() => { if (SelectedItem == null && FilteredItems.Count == 1) { SelectedItem = FilteredItems[0]; } CloseOk?.Invoke(); }, () => SelectedItem != null || FilteredItems.Count == 1 || int.TryParse(SearchText, out _)); Cancel = new DelegateCommand(() => CloseCancel?.Invoke()); FilteredItems.ObserveCollectionChanges().Subscribe(_ => Accept.RaiseCanExecuteChanged()); this.WhenPropertyChanged(t => t.SearchText).Subscribe(_ => Accept.RaiseCanExecuteChanged()); this.WhenPropertyChanged(t => t.SelectedItem).Subscribe(_ => Accept.RaiseCanExecuteChanged()); }
public InputEntryProviderViewModel(string title, string description, T defaultValue, Func <T, bool>?isValid = null) { Description = description; entry = defaultValue; Title = title; Accept = Save = new DelegateCommand(() => { CloseOk?.Invoke(); }, () => isValid == null || isValid(Entry)).ObservesProperty(() => Entry); Cancel = new DelegateCommand(() => { CloseCancel?.Invoke(); }); }
public RowPickerViewModel(ViewModelBase baseViewModel, ITaskRunner taskRunner, ISolutionItemSqlGeneratorRegistry queryGeneratorRegistry, IClipboardService clipboardService, IWindowManager windowManager, IEventAggregator eventAggregator, ISolutionItemEditorRegistry solutionItemEditorRegistry, ISessionService sessionService, IMessageBoxService messageBoxService, ISolutionTasksService solutionTasksService, bool noSaveMode = false) { this.baseViewModel = baseViewModel; this.solutionItemEditorRegistry = solutionItemEditorRegistry; this.sessionService = sessionService; this.messageBoxService = messageBoxService; this.noSaveMode = noSaveMode; Watch(baseViewModel, o => o.IsModified, nameof(Title)); ExecuteChangedCommand = noSaveMode ? AlwaysDisabledCommand.Command : new AsyncAutoCommand(async() => { baseViewModel.Save.Execute(null); eventAggregator.GetEvent <DatabaseTableChanged>().Publish(baseViewModel.TableDefinition.TableName); await taskRunner.ScheduleTask("Update session", async() => await sessionService.UpdateQuery(baseViewModel)); if (solutionTasksService.CanReloadRemotely) { await solutionTasksService.ReloadSolutionRemotelyTask(baseViewModel.SolutionItem); } }); CopyCurrentSqlCommand = new AsyncAutoCommand(async() => { await taskRunner.ScheduleTask("Generating SQL", async() => { clipboardService.SetText((await baseViewModel.GenerateQuery()).QueryString); }); }); GenerateCurrentSqlCommand = new AsyncAutoCommand(async() => { var sql = await baseViewModel.GenerateQuery(); var item = new MetaSolutionSQL(new JustQuerySolutionItem(sql.QueryString)); var editor = solutionItemEditorRegistry.GetEditor(item); await windowManager.ShowDialog((IDialog)editor); }); PickSelected = new AsyncAutoCommand(async() => { await AskIfSave(false); }); Cancel = new DelegateCommand(() => { CloseCancel?.Invoke(); }); Accept = PickSelected; }
public StringPickerViewModel(string existing, bool acceptEmpty, bool multiline) { this.acceptEmpty = acceptEmpty; MultiLine = multiline; content = existing; Accept = new DelegateCommand(() => { CloseOk?.Invoke(); }, () => acceptEmpty || !string.IsNullOrWhiteSpace(Content)).ObservesProperty(() => Content); Cancel = new DelegateCommand(() => { CloseCancel?.Invoke(); }); }
public NewItemDialogViewModel(ISolutionItemProvideService provider, ICurrentCoreVersion currentCore) { Dictionary <string, NewItemPrototypeGroup> groups = new(); Categories = new ObservableCollection <NewItemPrototypeGroup>(); bool coreIsSpecific = currentCore.IsSpecified; foreach (var item in provider.All) { if (!groups.TryGetValue(item.GetGroupName(), out var group)) { group = new NewItemPrototypeGroup(item.GetGroupName()); groups[item.GetGroupName()] = group; Categories.Add(group); } bool isCompatible = item.IsCompatibleWithCore(currentCore.Current); if (!isCompatible && coreIsSpecific) { continue; } var info = new NewItemPrototypeInfo(item, isCompatible); group.Add(info); } Accept = new DelegateCommand(() => { CloseOk?.Invoke(); }); Cancel = new DelegateCommand(() => { CloseCancel?.Invoke(); }); Categories.RemoveIf(c => c.Count == 0); if (Categories.Count > 0) { SelectedCategory = Categories[0]; } if (Categories.Count > 0 && Categories[0].Count > 0) { SelectedPrototype = Categories[0][0]; } }
public NewItemDialogViewModel(IEnumerable <ISolutionItemProvider> items, ICurrentCoreVersion coreVersion) { ItemPrototypes = new ObservableCollection <NewItemPrototypeInfo>(); foreach (var item in items.Where(i => i.IsCompatibleWithCore(coreVersion.Current))) { ItemPrototypes.Add(new NewItemPrototypeInfo(item)); } Accept = new DelegateCommand(() => { CloseOk?.Invoke(); }); Cancel = new DelegateCommand(() => { CloseCancel?.Invoke(); }); }
public ItemFromListProviderViewModel(Dictionary <int, SelectOption> items, bool asFlags, int?current = null) { this.asFlags = asFlags; RawItems = new ObservableCollection <KeyValuePair <int, CheckableSelectOption> >(); foreach (int key in items.Keys) { bool isSelected = current.HasValue && ((current == 0 && key == 0) || (key > 0) && (current & key) == key); var item = new KeyValuePair <int, CheckableSelectOption>(key, new CheckableSelectOption(items[key], isSelected)); if (isSelected) { SelectedItem = item; } RawItems.Add(item); } Columns = new ObservableCollection <ColumnDescriptor> { new("Key", "Key", 50), new("Name", "Value.Name"), new("Description", "Value.Description") }; if (asFlags) { Columns.Insert(0, new ColumnDescriptor("", "Value.IsChecked", null, true)); } this.items = new CollectionViewSource(); this.items.Source = RawItems; this.items.Filter += ItemsOnFilter; if (items.Count == 0) { SearchText = current.HasValue ? current.Value.ToString() : ""; } ShowItemsList = items.Count > 0; DesiredHeight = ShowItemsList ? 470 : 130; Accept = new DelegateCommand(() => CloseOk?.Invoke()); Cancel = new DelegateCommand(() => CloseCancel?.Invoke()); }
public ItemFromListProviderViewModel(Dictionary <T, SelectOption>?items, IComparer <CheckableSelectOption <T> > comparer, Func <T, bool> shouldBeSelected, bool asFlags, T?current = default) { Items = items; this.asFlags = asFlags; this.items = AutoDispose(new SourceList <CheckableSelectOption <T> >()); ReadOnlyObservableCollection <CheckableSelectOption <T> > outFilteredList; currentFilter = AutoDispose(new ReactiveProperty <Func <CheckableSelectOption <T>, bool> >(_ => true, Compare.Create <Func <CheckableSelectOption <T>, bool> >((_, _) => false, _ => 0))); AutoDispose(this.items.Connect() .Filter(currentFilter) .Sort(comparer) .Bind(out outFilteredList) .Subscribe()); FilteredItems = outFilteredList; if (items != null) { this.items.Edit(list => { foreach (T key in items.Keys) { bool isSelected = shouldBeSelected(key); var item = new CheckableSelectOption <T>(key, items[key], isSelected); if (isSelected) { SelectedItem = item; } list.Add(item); } }); } Columns = new ObservableCollection <ColumnDescriptor> { new("Key", "Entry", 80), new("Name", "Name", 220), new("Description", "Description", 320) }; if (asFlags) { Columns.Insert(0, new ColumnDescriptor("", "IsChecked", 35, true)); } if (items == null || items.Count == 0) { SearchText = current != null?current.ToString() ?? "" : ""; } ShowItemsList = items?.Count > 0; DesiredHeight = ShowItemsList ? 670 : 130; DesiredWidth = ShowItemsList ? 800 : 400; Accept = new DelegateCommand(() => { if (SelectedItem == null && FilteredItems.Count == 1) { SelectedItem = FilteredItems[0]; } CloseOk?.Invoke(); }, () => asFlags || SelectedItem != null || FilteredItems.Count == 1 || int.TryParse(SearchText, out _)); Cancel = new DelegateCommand(() => CloseCancel?.Invoke()); FilteredItems.ObserveCollectionChanges().Subscribe(_ => Accept.RaiseCanExecuteChanged()); this.WhenPropertyChanged(t => t.SearchText).Subscribe(_ => Accept.RaiseCanExecuteChanged()); this.WhenPropertyChanged(t => t.SelectedItem).Subscribe(_ => Accept.RaiseCanExecuteChanged()); }
public ConditionsEditorViewModel( IConditionsFactory conditionsFactory, IConditionDataManager conditionDataManager, IItemFromListProvider itemFromListProvider, IHistoryManager historyManager, IEnumerable <ICondition>?conditions, int conditionSourceType) { this.conditionsFactory = conditionsFactory; this.conditionDataManager = conditionDataManager; this.HistoryManager = historyManager; ConditionTypes = conditionDataManager .GetConditionGroups() .SelectMany(group => group.Members) .Where(conditionDataManager.HasConditionData) .Select(conditionDataManager.GetConditionData) .ToList(); if (conditions != null) { int previousElseGroup = -1; foreach (var c in conditions) { var vm = conditionsFactory.Create(conditionSourceType, c); if (vm == null) { continue; } if (c.ElseGroup != previousElseGroup && previousElseGroup != -1) { Conditions.Add(conditionsFactory.CreateOr(conditionSourceType)); } previousElseGroup = c.ElseGroup; Conditions.Add(vm); } } Accept = new DelegateCommand(() => CloseOk?.Invoke()); Cancel = new DelegateCommand(() => CloseCancel?.Invoke()); PickCommand = new AsyncAutoCommand <ParameterValueHolder <long> >(async prh => { if (!prh.HasItems) { return; } var newItem = await itemFromListProvider.GetItemFromList(prh.Parameter.Items, prh.Parameter is FlagParameter, prh.Value); if (newItem.HasValue) { prh.Value = newItem.Value; } }); AddItemCommand = new DelegateCommand(() => { int index = Conditions.Count; if (SelectedCondition != null) { index = Conditions.IndexOf(SelectedCondition) + 1; } index = Math.Clamp(index, 0, Conditions.Count); Conditions.Insert(index, conditionsFactory.Create(conditionSourceType, 0) ?? conditionsFactory.CreateOr(conditionSourceType)); }); RemoveItemCommand = new DelegateCommand(() => { if (SelectedCondition == null) { return; } int indexOf = Conditions.IndexOf(SelectedCondition); if (indexOf != -1) { Conditions.RemoveAt(indexOf); if (indexOf - 1 >= 0 && Conditions.Count > 0) { SelectedCondition = Conditions[indexOf - 1]; } else if (Conditions.Count > 0) { SelectedCondition = Conditions[indexOf]; } } }, () => SelectedCondition != null).ObservesProperty(() => SelectedCondition); CopyCommand = new DelegateCommand(() => { if (SelectedCondition != null) { Clipboard = SelectedCondition?.ToCondition(0); } }, () => SelectedCondition != null).ObservesProperty(() => SelectedCondition); CutCommand = new DelegateCommand(() => { if (SelectedCondition != null) { Clipboard = SelectedCondition?.ToCondition(0); Conditions.Remove(SelectedCondition !); SelectedCondition = null; } }, () => SelectedCondition != null).ObservesProperty(() => SelectedCondition); PasteCommand = new DelegateCommand(() => { if (clipboard != null) { int indexOf = Conditions.Count; if (SelectedCondition != null) { indexOf = Conditions.IndexOf(SelectedCondition) + 1; } var item = conditionsFactory.Create(conditionSourceType, clipboard); if (item != null) { Conditions.Insert(indexOf, item); } } }, () => Clipboard != null).ObservesProperty(() => Clipboard); UndoCommand = new DelegateCommand(HistoryManager.Undo, () => HistoryManager.CanUndo).ObservesProperty(() => HistoryManager.CanUndo); RedoCommand = new DelegateCommand(HistoryManager.Redo, () => HistoryManager.CanRedo).ObservesProperty(() => HistoryManager.CanRedo); Watch(this, t => t.SelectedCondition, nameof(SelectedConditionsType)); historyHandler = AutoDispose(new ConditionsEditorHistoryHandler(this, conditionsFactory)); HistoryManager.AddHandler(historyHandler); }
public PacketFilterDialogViewModel(IClipboardService clipboardService, IReadOnlyFilterData?filterData) { this.clipboardService = clipboardService; Accept = new DelegateCommand(() => CloseOk?.Invoke()); Cancel = new DelegateCommand(() => CloseCancel?.Invoke()); ClearFiltersCommand = new AsyncAutoCommand(async() => { ExcludedEntries = ""; IncludedEntries = ""; ExcludedOpcodes = ""; IncludedOpcodes = ""; ExcludedGuids.Clear(); IncludedGuids.Clear(); CommaSeparatedPackets = ""; HasMaxPacketNumber = false; HasMinPacketNumber = false; MinPacketNumber = 0; MaxPacketNumber = 0; }); CopyFiltersCommand = new AsyncAutoCommand(async() => { var data = new SerializedClipboardData() { ExcludedEntries = GenerateList(excludedEntries, s => uint.TryParse(s, out _), uint.Parse), IncludedEntries = GenerateList(includedOpcodes, s => uint.TryParse(s, out _), uint.Parse), ExcludedGuids = ExcludedGuids.Count == 0 ? null : ExcludedGuids.Select(s => s.ToHexWithTypeString()).ToList(), IncludedGuids = IncludedGuids.Count == 0 ? null : IncludedGuids.Select(s => s.ToHexWithTypeString()).ToList(), ExcludedOpcodes = GenerateList(excludedOpcodes, _ => true, s => s.ToUpper()), IncludedOpcodes = GenerateList(includedOpcodes, _ => true, s => s.ToUpper()), ForceIncludedNumbers = GenerateList(commaSeparatedPackets, s => int.TryParse(s, out var x) && x >= 0, int.Parse), MinPacket = HasMinPacketNumber ? MinPacketNumber : null, MaxPacket = HasMaxPacketNumber ? MaxPacketNumber : null, }; var serialized = JsonConvert.SerializeObject(data, Formatting.Indented, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }); clipboardService.SetText(serialized); }); PasteFiltersCommand = new AsyncAutoCommand(async() => { var text = await clipboardService.GetText(); if (string.IsNullOrEmpty(text)) { return; } SerializedClipboardData?serialized = null; try { serialized = JsonConvert.DeserializeObject <SerializedClipboardData>(text); } catch (Exception _) { // ignored } if (!serialized.HasValue) { return; } HasMinPacketNumber = serialized.Value.MinPacket.HasValue; HasMaxPacketNumber = serialized.Value.MaxPacket.HasValue; MinPacketNumber = serialized.Value.MinPacket ?? 0; MaxPacketNumber = serialized.Value.MaxPacket ?? 0; if (serialized.Value.ExcludedEntries != null) { ExcludedEntries = string.Join(", ", serialized.Value.ExcludedEntries); } if (serialized.Value.IncludedEntries != null) { IncludedEntries = string.Join(", ", serialized.Value.IncludedEntries); } if (serialized.Value.ExcludedGuids != null) { ExcludedGuids.AddRange(serialized.Value.ExcludedGuids.StringToGuids()); } if (serialized.Value.IncludedGuids != null) { IncludedGuids.AddRange(serialized.Value.IncludedGuids.StringToGuids()); } if (serialized.Value.ExcludedOpcodes != null) { ExcludedOpcodes = string.Join(", ", serialized.Value.ExcludedOpcodes); } if (serialized.Value.IncludedOpcodes != null) { IncludedOpcodes = string.Join(", ", serialized.Value.IncludedOpcodes); } if (serialized.Value.ForceIncludedNumbers != null) { CommaSeparatedPackets = string.Join(", ", serialized.Value.ForceIncludedNumbers); } }); if (filterData != null) { HasMinPacketNumber = filterData.MinPacketNumber.HasValue; HasMaxPacketNumber = filterData.MaxPacketNumber.HasValue; MinPacketNumber = filterData.MinPacketNumber ?? 0; MaxPacketNumber = filterData.MaxPacketNumber ?? 0; if (filterData.ExcludedEntries != null) { ExcludedEntries = string.Join(", ", filterData.ExcludedEntries); } if (filterData.IncludedEntries != null) { IncludedEntries = string.Join(", ", filterData.IncludedEntries); } if (filterData.ExcludedGuids != null) { ExcludedGuids.AddRange(filterData.ExcludedGuids); } if (filterData.IncludedGuids != null) { IncludedGuids.AddRange(filterData.IncludedGuids); } if (filterData.ExcludedOpcodes != null) { ExcludedOpcodes = string.Join(", ", filterData.ExcludedOpcodes); } if (filterData.IncludedOpcodes != null) { IncludedOpcodes = string.Join(", ", filterData.IncludedOpcodes); } if (filterData.ForceIncludePacketNumbers != null) { CommaSeparatedPackets = string.Join(", ", filterData.ForceIncludePacketNumbers); } } DeleteIncludedGuid = new DelegateCommand <UniversalGuid?>(guid => { if (guid != null) { IncludedGuids.Remove(guid); } }); DeleteExcludedGuid = new DelegateCommand <UniversalGuid?>(guid => { if (guid != null) { ExcludedGuids.Remove(guid); } }); AutoDispose(this.ToObservable(o => o.ExcludedEntries).SubscribeAction(_ => RaisePropertyChanged(nameof(EntriesHeader)))); AutoDispose(this.ToObservable(o => o.IncludedEntries).SubscribeAction(_ => RaisePropertyChanged(nameof(EntriesHeader)))); AutoDispose(ExcludedGuids.ToCountChangedObservable().SubscribeAction(_ => RaisePropertyChanged(nameof(GuidsHeader)))); AutoDispose(IncludedGuids.ToCountChangedObservable().SubscribeAction(_ => RaisePropertyChanged(nameof(GuidsHeader)))); AutoDispose(this.ToObservable(o => o.ExcludedOpcodes).SubscribeAction(_ => RaisePropertyChanged(nameof(OpcodesHeader)))); AutoDispose(this.ToObservable(o => o.IncludedOpcodes).SubscribeAction(_ => RaisePropertyChanged(nameof(OpcodesHeader)))); }
public ItemFromListProviderViewModel(Dictionary <long, SelectOption> items, bool asFlags, long?current = null) { this.asFlags = asFlags; this.items = AutoDispose(new SourceList <CheckableSelectOption>()); ReadOnlyObservableCollection <CheckableSelectOption> outFilteredList; currentFilter = AutoDispose(new ReactiveProperty <Func <CheckableSelectOption, bool> >(_ => true, Compare.Create <Func <CheckableSelectOption, bool> >((_, _) => false, _ => 0))); AutoDispose(this.items.Connect() .Filter(currentFilter) .Sort(Comparer <CheckableSelectOption> .Create((x, y) => x.Entry.CompareTo(y.Entry))) .Bind(out outFilteredList) .Subscribe()); FilteredItems = outFilteredList; this.items.Edit(list => { foreach (long key in items.Keys) { bool isSelected = current.HasValue && ((current == 0 && key == 0) || (key > 0) && (current & key) == key); var item = new CheckableSelectOption(key, items[key], isSelected); if (isSelected) { SelectedItem = item; } list.Add(item); } }); Columns = new ObservableCollection <ColumnDescriptor> { new("Key", "Entry", 50), new("Name", "Name"), new("Description", "Description") }; if (asFlags) { Columns.Insert(0, new ColumnDescriptor("", "IsChecked", 35, true)); } if (items.Count == 0) { SearchText = current.HasValue ? current.Value.ToString() : ""; } ShowItemsList = items.Count > 0; DesiredHeight = ShowItemsList ? 470 : 130; Accept = new DelegateCommand(() => { if (SelectedItem == null && FilteredItems.Count == 1) { SelectedItem = FilteredItems[0]; } CloseOk?.Invoke(); }, () => asFlags || SelectedItem != null || FilteredItems.Count == 1 || int.TryParse(SearchText, out _)); Cancel = new DelegateCommand(() => CloseCancel?.Invoke()); FilteredItems.ObserveCollectionChanges().Subscribe(_ => Accept.RaiseCanExecuteChanged()); this.WhenPropertyChanged(t => t.SearchText).Subscribe(_ => Accept.RaiseCanExecuteChanged()); this.WhenPropertyChanged(t => t.SelectedItem).Subscribe(_ => Accept.RaiseCanExecuteChanged()); }
public VariablePickerViewModel(GlobalVariableType type, SmartScriptBase script, long?initial) { this.type = type; this.script = script; Items = new(script.GlobalVariables.Where(gv => gv.VariableType == type) .OrderBy(i => i.Key) .Distinct(Compare.By <GlobalVariable, long>(i => i !.Key)) .Select(gv => new VariableItemViewModel(gv))); SelectedItem = initial.HasValue ? Items.FirstOrDefault(i => i.Id == initial) : null; if (SelectedItem == null && initial != null) { var phantom = VariableItemViewModel.CreatePhantom(initial.Value); Items.Add(phantom); SelectedItem = phantom; } Cancel = new DelegateCommand(() => { CloseCancel?.Invoke(); }); Accept = new DelegateCommand(() => { var dict = Items.Where(i => !i.IsPhantom).SafeToDictionary(i => i.OriginalId ?? i.Id, i => i); for (var i = script.GlobalVariables.Count - 1; i >= 0; i--) { var existing = script.GlobalVariables[i]; if (existing.VariableType == type) { if (dict.TryGetValue(existing.Key, out var item)) { existing.Key = item.Id; existing.Name = item.Name; existing.Comment = item.Comment; dict.Remove(item.OriginalId ?? item.Id); } else { script.GlobalVariables.RemoveAt(i); } } } foreach (var pair in dict) { script.GlobalVariables.Add(new GlobalVariable() { Key = pair.Key, Name = pair.Value.Name, Comment = pair.Value.Comment, VariableType = type }); } CloseOk?.Invoke(); }); AddItemCommand = new DelegateCommand(() => { var phantom = VariableItemViewModel.CreatePhantom((Items.Count > 0 ? Items.Max(i => i.Id) : 0) + 1); Items.Add(phantom); SelectedItem = phantom; }); RemoveItemCommand = new DelegateCommand(() => { if (SelectedItem != null) { Items.Remove(SelectedItem); SelectedItem = null; } }, () => SelectedItem != null).ObservesProperty(() => SelectedItem); Items.DisposeOnRemove(); AutoDispose(new ActionDisposable(() => Items.Clear())); }