how to fill in combobox dynamically with caliburn micro? - caliburn.micro

I am new to MVVM and Caliburn.Micro.
I am trying to do a simple application using Caliburn.Micro that has a combobox.
The combobox items are added dynamically when the program is running, the problem is that the combobox stops updating its content after the first item navigation.
In View:
The combobox is declared as follows:
<ComboBox x:Name="cmbProductList" />
In ViewModel:
I declare the following list for combobox items:
List<string> L = new List<string> { };
I am using the following method to fill combobox item:
public List<string> cmbProductList
{
get
{
return L;
}
set
{
L = value;
NotifyOfPropertyChange("cmbProductList");
}
}
And this command to add an item to this list:
L.Add(p1.ID.ToString());

The container you are using for your combobox items is not the proper one. List has no way of notifying the UI of its changes (items added or removed). Instead you need to use an ObservableCollection. Or even better, you can use BindableCollection that is Caliburn.Micro's customized version of ObservableCollection:
BindableCollection is a simple collection that inherits from ObservableCollection, but that ensures that all its events are raised on the UI thread as well.
http://caliburnmicro.com/documentation/introduction
Your property would look like this:
private BindableCollection<string> _cmbProductList;
public BindableCollection<string> cmbProductList
{
get
{
return _cmbProductList;
}
set
{
_cmbProductList = value;
NotifyOfPropertyChange("cmbProductList");
}
}
and of course its initialization would look like this:
_cmbProductList = new BindableCollection<string>();
The rest can stay unchanged.

Related

Xamarin forms template switch native content remains

I am seeing something strange where an instance of ContentView from a ControlTemplate affects how another instance of the ContentView looks, under the same page, from another ControlTemplate.
ContentView BLA:
ScrollView
L Accordion control
L Accordion Item
Control template A:
BLA
Control template B:
Tab control
L Tab 1 Content: BLA
L Tab 2 Content: other
Somehow switching ControlTemplate from A to B, B to A,
And the height of the accordion item remain from the first template.
It is not the same instance.
How can I make sure the native controls are disposed of when switching ControlTemplate?
Normally, we use DataTemplateSelector to switch templates.
For the Limitations in the MSdocs, the warnings, however the reason behind it, is due to the caching of templates in the renderer. If you change it up, the previous template will be cached in the renderer and there will be no longer any way to reference or dispose of it, until the ListView is disposed of. Hence as you scroll, it will continue to cache new templates and never free up the old ones.
Hope this would be helpful to you.
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/templates/data-templates/selector
I moved away from template switching as the template is being cached, meaning the last view is still in memory, and this causes all kind of problems such as binding errors and layout measures turning out wrong.
I now use MyView which inherits ContentView:
public class MyView : ContentView
{
public static readonly BindableProperty DataProperty =
BindableProperty.Create(nameof(Data), typeof(object), typeof(MyView ), null, propertyChanging: OnDataPropertyChanging);
public object Data
{
get => (object)GetValue(DataProperty);
set => SetValue(DataProperty, value);
}
private static void OnDataPropertyChanging(BindableObject bindable, object oldValue, object newValue)
{
var instance = bindable as MyViewModel;
instance.Content = null;
if (newValue == null)
{
return;
}
var type = newValue.GetType();
View view = null;
if (type == typeof(MyViewModel1))
{
view = new BLA();
}
else if (type == typeof(MyViewModel2))
{
view = new other();
}
if (view != null)
{
view.BindingContext = newValue;
instance.Content = view;
}
}
}

How can I listen to INTERNAL changes in a tableview?

I have a TableView whose items contain checkboxes. As soon as 2 checkboxes are selected, I need to "unhide" a button.
I have no idea how to check that. Do you have an approach?
The items don't know each other.
The TableView-Controller holds the TableView and the TableColumns.
As far as I know you cannot use bindings here, since you cannot bind yourself to multiple properties. I'm glad for every kind of help. :)
EDIT: To clarify myself: tableView.getItems().addListener() won't work since this can only listen to modifications to the list and not to the outer elements. It can notice if "add()" or "remove" was called, but that's basically it as far as I know.
PS: Busy waiting in a seperate thread is no solution of course.
Assuming you have a TableView<Item> for some Item class with a BooleanProperty:
public class Item {
private final BooleanProperty checked = new SimpleBooleanProperty();
public BooleanProperty checkedProperty() {
return checked ;
}
public final boolean isChecked() {
return checkedProperty().get();
}
public final void setChecked(boolean checked) {
checkedProperty().set(checked);
}
// other properties, etc...
}
and your checkboxes are bound to this property, then you can create your items list using an extractor:
ObservableList<Item> items = FXCollections.observableArrayList(item ->
new Observable[] { item.checkedProperty() });
table.setItems(items);
This ensures that the list fires update notifications when the checkedProperty changes on any of its elements.
So now you can just do normal binding stuff like:
IntegerBinding numberChecked = Bindings.createIntegerBinding(() ->
(int) items.stream().filter(Item::isChecked).count(),
items);
button.visibleProperty().bind(numberChecked.greaterThanOrEqualTo(2));
If you want to be super-efficient:
int requiredNumberChecked = 2 ;
button.visibleProperty().bind(Bindings.createBooleanBinding(() ->
items.stream()
.filter(Item::isSelected)
.skip(requiredNumberChecked-1)
.findAny().isPresent(),
items));
(the binding will return true as soon as it finds two checked items, instead of scanning the entire list).

Not being able to change Title in a Caliburn.Micro Conductor View using MahApps MetroWindow

I'm doing like so:
<Controls:MetroWindow x:Class="BS.Expert.Client.App.Views.ShellView"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
ShowTitleBar="True"
Title="My Title">
The thing is that this is at the same time a defined main conductor on the main window with which I control navigation through other windows, so I'm not able to inherit from MetroWindow to at least trying change the title in the ViewModel:
public class ShellViewModel : Conductor<IScreen>.Collection.OneActive, IShell
{
public ShellViewModel()
{
#region mahApps Theme Loading
var theme = ThemeManager.DetectAppStyle(Application.Current);
var appTheme = ThemeManager.GetAppTheme(App.Configuration.Theme);
ThemeManager.ChangeAppStyle(Application.Current, theme.Item2, appTheme);
#endregion
//TODO: Changing Title here is not possible ((MetroWindow)this).Title = "No way";
// Tudo bem na seguinte liƱa
LocalizeDictionary.Instance.Culture = new System.Globalization.CultureInfo("pt-BR");
ShowPageOne();
}
public void ShowPageOne()
{
ActivateItem(new PageOneViewModel());
}
}
How should I change the title?
When using the MVVM pattern you should never try to set anything on the view directly in the view model like this. Instead using data binding to accomplish this.
So you would have a property on your ShellViewModel with something like:
public string MyTitle
{
get { return _myTitle; }
set
{
_myTitle = value;
//Insert your property change code here (not sure of the caliburn micro version)
}
}
and in your window xaml it would be something like:
<Controls:MetroWindow
Title="{Binding MyTitle}"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
...
>

.NET 4.0 DataGridCombobox SelectionChanged issue

I have a requirement in my program that the object bound (from ViewModel) in a Combobox is updated as soon as an item is selected in the combobox. Currently, the object only updates once the edit is committed by either pressing Enter or leaving the cell. The user does not want the extra step.
My thought would be to have the act of selecting an item in the combobox trigger the CommitEdit() method and then CancelEdit(). However, I cannot seem to find a way to hook into the SelectionChanged event for the DataGridComboBoxColumn as it is not available.
Other suggestions have been to listen in the viewmodel for a property change event but the property is not changed until the Cell Edit is finished.
Can anyone think of a way to cause the selection of a new item (index) in a DataGridCombobox to close the edit of the cell as if the user pressed Enter or left the cell?
NOTE: I cannot use .NET 4.5 due to customer limitations.
I've had similar issue but i just found out the solution using attached property, This may not exactly fix your problem but it will help in datagrid selection changed issue.
Below is the attached property and handler methods
public static readonly DependencyProperty ComboBoxSelectionChangedProperty = DependencyProperty.RegisterAttached("ComboBoxSelectionChangedCommand",
typeof(ICommand),
typeof(SpDataGrid),
new PropertyMetadata(new PropertyChangedCallback(AttachOrRemoveDataGridEvent)));
public static void AttachOrRemoveDataGridEvent(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
DataGrid dataGrid = obj as DataGrid;
if (dataGrid != null)
{
if (args.Property == ComboBoxSelectionChangedProperty)
{
dataGrid.SelectionChanged += OnComboBoxSelectionChanged;
}
}
else if (args.OldValue != null && args.NewValue == null)
{ if (args.Property == ComboBoxSelectionChangedProperty)
{
dataGrid.SelectionChanged -= OnComboBoxSelectionChanged;
}
}
}
private static void OnComboBoxSelectionChanged(object sender, SelectionChangedEventArgs args)
{
DependencyObject obj = sender as DependencyObject;
ICommand cmd = (ICommand)obj.GetValue(ComboBoxSelectionChangedProperty);
DataGrid grid = sender as DataGrid;
if (args.OriginalSource is ComboBox)
{
if (grid.CurrentCell.Item != DependencyProperty.UnsetValue)
{
//grid.CommitEdit(DataGridEditingUnit.Row, true);
ExecuteCommand(cmd, grid.CurrentCell.Item);
}
}
}
SpDataGrid is the custom control that i inherited from data grid.
I added below style in generic.xaml as i use the resourcedictionary for style (you can certainly add inside the datagrid).
<Style TargetType="{x:Type Custom:SpDataGrid}">
<Setter Property="Custom:SpDataGrid.ComboBoxSelectionChangedCommand" Value="{Binding ComboBoxSelectionChanged}"/>
</Style>
ComboBoxSelectionChanged is the command in my viewmodel. OnComboBoxSelectionChanged i commented the commitedit because in my case the values were already updated.
Let me know if anything is not clear or any questions. Hope this helps.

ItemClick Event in a flex Combobox

Does anyone know, is there any way to catch ItemClick Event in a Flex ComboBox (or anything similar). Maybe there's any trick .. :) I do realize, that I can customize it, but this not suits my case.
Thanks for your time :)
As you can see in mx:ComboBox sources, the function, creating the dropdown list, is private, the listener to ITEM_CLICK is private and the list itself is also private:
private var _dropdown:ListBase;
private function getDropdown():ListBase
{
// ...
_dropdown = dropdownFactory.newInstance();
// ...
_dropdown.addEventListener(ListEvent.ITEM_CLICK, dropdown_itemClickHandler);
// ....
}
private function dropdown_itemClickHandler(event:ListEvent):void
{
if (_showingDropdown)
{
close();
}
}
So you can not even extend ComboBox.
The only public thing is dropdownFactory, which theoretically can be overriden to somehow register the created dropdown list or create extended list. But the problem I see is that ComboBox is not the parent of dropdown list - PopupManager is. This can make dispatching (bubble) events quite difficult.
I think the following document will be helpful
ItemClick event in flex List
I found this solution. I just want a spark dropdownlist with itemClick event and without itemselect option (don't show selected item label on button)
[Event(name="itemClick", type="mx.events.ItemClickEvent")]
public class ItemClickDropDownList extends DropDownList
{
public function ItemClickDropDownList()
{
super();
}
override public function closeDropDown(commit:Boolean):void
{
super.closeDropDown(commit);
var e:ItemClickEvent = new ItemClickEvent(ItemClickEvent.ITEM_CLICK, true);
e.item = this.selectedItem;
e.index = this.selectedIndex;
dispatchEvent(e);
//Deselect item
this.selectedIndex = -1;
}

Resources