Good day. I'll try to make it shorter as possible. My XAML:
<StackPanel Grid.Row="1" Grid.Column="0" x:Name="stackActions">
<Button x:Name="btnAction_1" Margin="5" Content="Start work" Click="btnAction_1_Click"/>
<Button x:Name="btnAction_2" Margin="5" Content="" Visibility="Collapsed"/>
<Button x:Name="btnAction_3" Margin="5" Content="" Visibility="Collapsed"/>
<Button x:Name="btnAction_4" Margin="5" Content="" Visibility="Collapsed"/>
<Button x:Name="btnAction_5" Margin="5" Content="" Visibility="Collapsed"/>
</StackPanel>
My C#:
public partial class MainWindow : Window
{
// Method for collapsing all buttons
public void HideAllButtons()
{
stackActions.Children.OfType<Button>().ToList().ForEach(button => button.Visibility = Visibility.Collapsed);
}
public MainWindow()
{
InitializeComponent();
}
private void btnAction_1_Click(object sender, RoutedEventArgs e)
{
HideAllButtons();
btnAction_2.Visibility = Visibility.Visible;
btnAction_3.Visibility = Visibility.Visible;
btnAction_2.Content = "Surveys";
btnAction_3.Content = "ClearAll";
btnAction_2.Click += SeeSurveys;
btnAction_3.Click += ClearAll;
}
private void SeeSurveys(object sender, RoutedEventArgs e )
{
btnAction_2.Click -= SeeSurveys;
btnAction_3.Click -= ClearAll;
btnAction_2.Content = "Draft";
btnAction_3.Content = "OnHire";
btnAction_4.Visibility = Visibility.Visible;
btnAction_4.Content = "Condition";
btnAction_5.Visibility = Visibility.Visible;
btnAction_5.Content = "Back";
btnAction_5.Click += btnAction_1_Click;
}
private void ClearAll (object sender, RoutedEventArgs e)
{
HideAllButtons();
btnAction_1.Visibility = Visibility.Visible;
}
}
Actually this is works as i want, but looking like mess with much Click += and Click -= events. I understand that making more massive code confused myseld very soon. I suppose there are any options to bind click event for each button in XAML and change the event itself in C# or reduce the quantity of Visibility checks.
The main idea is changing click events and rewriting buttons content depending of what button was clicked.
So any advice is welcome to make this code more clear and short.
Like many wpf developers and the vast majority of commercial wpf teams, I use MVVM.
With that I would be thinking in terms of templating buttons out from viewmodels that had commands.
Rather than having many buttons where I switched the event handler round, I'd have lists of commands. These could be in a model which was a hierarchical form and a layer translated or picked from that if it suited your real world requirement best.
Or
A series of objects something like:
public class CommandListViewModel
{
public int Id { get; set; }
public ObservableCollection<CommandViewModel> Commands { get; set; }
}
public class CommandViewModel
{
public string Name { get; set; }
public DelegateCommand<MainWindowViewModel> Command { get; set; }
}
These are just sketched out to give an idea of what I have in mind. You would have inotifypropertychanged implemented in these viewmodels and probably want to raise change notification from the property setters.
But the idea is each level is represented by a class commandlistviewmodel which has an id and a bunch of commands with the "name". The name is displayed in a button which has that command.
Each command gets a reference to mainwindowviewmodel passed into it. So it can access properties on that. One of which would be a list or instance of commandlistviewmodel.
Hence any given command could go find a list of commands it needs to "substitute" or some other property in mainwindowviewmodel it should manipulate.
You can create delegatecommand ( or relaycommand ) using lambdas.
Rather or as well as passing in an instance of mainwindowviewmodel these could potentially capture variables from wherever they are constructed. You could make that a mediator or event aggregator which abstracted UI services.
Essentially though.
These commands do what your click handlers do.
These lists of commands are your various switchable levels.
You work with these and bind them to the UI to template into a bunch of buttons.
The way you do that templating is in an itemscontrol. Or a listbox if you wanted to have a built in slider and maybe indicate which one was clicked last using selecteditem.
<ItemsControl ItemsSource="{Binding Commands}"
>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:CommandViewModel}">
<Button Content="{Binding Name}"
Command="{Binding Command}"
CommandParameter="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
With the above a viewmodel which is the datacontext of this itemscontrol ( usually also of the entire window ) would have a public property which was an observablecollection of CommandViewModel. The items or the entire collection are switched out depending on the "level" chosen. You click one command which chooses Surveys and a list of commandviewmodel appropriate for Surveys are shown. One of those looks like it'd be a "Back" command which switched back to the previous list. And so on.
You could also have a property in a commandviewmodel which binds to Visibility so you could collapse a button. I'm not sure you'd need that with this pattern though. It will automatically cope with a "level" which has 3, 5, 10 or whatever number of commands.
You will have a fair bit of reading to do before you would be in a position to switch from your current pattern to mvvm. Delegatecommand is just one of your options, it's from Prism and there's a prism.mvvm nuget you could consider. Or you might prefer mvvmlight which has relaycommand. You'll want to use one of these or some other such framework to save you writing your own icommand implementation.
I hope that's sufficient to at least get you started.
Related
I'm currently building the UI for the app I'm working on and I have a few problems with the bindings.
The Scenario:
I have a pivot control with every pivot element consisting of an extra Frame/Page.
Now I have a TextBlock on the first PivotItem. I bind this to a "string" and use a button to switch between two possible contents of the button.
When the button is on the same Page/Frame it works like a charm. But when I implement a button on the MainPage and implement the same Viewmodel for the MainPage then it doesn't work. It will only change the string content on the MainPage.
Is it possible to implement the change for every Page/Frame?
And when that is done I have a Page where I gather data with a serial port.
I save the data to a List and I want to be able to use this list from 2 different Pages/Frames.
Thinking about the scenarion above then it would probably gather the data for the page where I have the button to get the data but it would probably display nothing on the other page.
How can I build it like I want it to be?
Here is a short example:
Mainpage.xaml
<StackPanel>
<Button Height="50" Width="200" Content="Change" FontSize="30" FontWeight="Bold" Margin="50 50 0 0" Click="{x:Bind MainViewModel.Change}"/>
<Pivot x:Name="MainPivot" Margin="50 50">
<PivotItem Header="Page 1">
<Frame x:Name="Page1" />
</PivotItem>
</Pivot>
</StackPanel>
Mainpage.xaml.cs
public MainPage()
{
this.InitializeComponent();
Page1.Navigate(typeof(Page1));
ViewModel = new MainViewModel();
}
public MainViewModel ViewModel { get; private set; }
Page1.xaml
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="{x:Bind Path=ViewModel.StringModel.String1, Mode=TwoWay}" FontSize="50" FontWeight="Bold" />
<Button Content="Change" FontSize="30" FontWeight="Bold" Click="{x:Bind ViewModel.Change}"/>
</StackPanel>
Page1.xaml.cs
public Page1()
{
this.InitializeComponent();
ViewModel = new MainViewModel();
}
public MainViewModel ViewModel { get; private set; }
MainViewModel.cs
private StringModel _stringModel = new StringModel();
public StringModel StringModel
{
get => _stringModel;
set
{
if (_stringModel != value)
{
_stringModel = value;
OnPropertyChanged();
}
}
}
public void Change()
{
if (StringModel.String1 == "Text1")
{
StringModel.String1 = "Text2";
}
else
{
StringModel.String1 = "Text1";
}
}
StringModel.cs
private string _string1 = "XXX";
public string String1
{
get => _string1;
set
{
_string1 = value;
OnPropertyChanged();
}
}
Sounds like you are missing a "service layer" or "business layer" of your application. You need an external class which manages the data, and can provide models to populate your ViewModels:
I'd suggest using some kind of dependency injection, so each of your page view models have a reference to the DataProvider service class. This class does the serial port work to get a list of models, and provides an interface for getting data and pushing any updates to the ViewModels.
A good way of handling events that are shared, like say a "load data" button that may appear on different view models is an Event Aggregator. A service that can be injected into classes where events can be raised or subscribed to across the application.
Generally children of a XAML parent inherit the binding context of said parent.
So not sure you need to hook up a VM to your frame.
But suppose it does not work with Frames, you are creating a new MainViewModel for the frame as for the mainpage!
The solution here would be to create a singleton MainViewModel and get a hold of that one to hook up the BindingContext.
You can use Publisher-Subscriber pattern (Pub-Sub).
Already explained this here: Communication Between Views in MVVM (Pub-Sub Pattern)
I created a user control that looks like a tile. Created another user control named TilePanel that serves as the default container of the tiles. And lastly, the very UI that looks like a Window start screen. I used RelayCommand to bind my TileCommands
Here are the codes:
Tilev2.xaml
<UserControl x:Class="MyNamespace.Tilev2"
Name="Tile"....
>
...
<Button x:Name="btnTile" Style="{StaticResource TileStyleButton}" Command="{Binding ElementName=Tile, Path=TileClickCommand}" >
</Button>
</UserControl>
Tilev2.xaml.cs
public partial class Tilev2 : UserControl
{
public Tilev2()
{
InitializeComponent();
}
//other DPs here
public ICommand TileClickCommand
{
get { return (ICommand)GetValue(TileClickCommandProperty); }
set { SetValue(TileClickCommandProperty, value); }
}
// Using a DependencyProperty as the backing store for TileClickCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TileClickCommandProperty =
DependencyProperty.Register("TileClickCommand", typeof(ICommand), typeof(Tilev2));
}
}
Then I created a TilePanel user control as the container of the tiles
TilePanel.xaml
<UserControl x:Class="MyNamespace.TilePanel"
...
>
<Grid>
<ScrollViewer>
<ItemsControl Name="tileGroup"
ItemsSource="{Binding TileModels}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local2:Tilev2 TileText="{Binding Text}"
TileIcon="{Binding Icon}"
TileSize="{Binding Size}"
TileFontSize="{Binding FontSize}"
Background="{Binding Background}"
TileCaption="{Binding TileCaption}"
TileCaptionFontSize="{Binding TileCaptionFontSize}"
TileClickCommand="{Binding TileCommand}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</UserControl>
TilePanel.xaml.cs
public partial class TilePanel : UserControl
{
public TilePanel()
{
InitializeComponent();
DataContext = new TilePanelViewModel();
}
public TilePanelViewModel ViewModel
{
get { return (TilePanelViewModel)this.DataContext; }
}
}
My ViewModel for TilePanel
TilePanelViewModel.cs
public class TilePanelViewModel : ViewModelBase
{
private ObservableCollection _tileModels;
public ObservableCollection<TileModel> TileModels
{
get
{
if (_tileModels == null)
_tileModels = new ObservableCollection<TileModel>();
return _tileModels;
}
}
}
Then my Tile model
TileModel.cs
public class TileModel : BaseNotifyPropertyChanged
{
//other members here
ICommand tileCommand { get; set; }
//other properties here
public ICommand TileCommand
{
get { return tileCommand; }
set { tileCommand = value; NotifyPropertyChanged("TileCommand"); }
}
}
}
This is my StartScreen View where TilePanels with tiles should be displayed...
StartScreen.xaml
<UserControl x:Class="MyNamespace.StartMenu"
... >
<Grid>
<DockPanel x:Name="dockPanel1" Grid.Column="0" Grid.Row="1" Margin="50,5,2,5">
<local:TilePanel x:Name="tilePanel"></local:TilePanel>
</DockPanel>
</Grid>
</UserControl>
StartScreen.xaml.cs
public partial class WincollectStartMenu : UserControl, IView<StartMenuViewModel>
{
public WincollectStartMenu()
{
InitializeComponent();
}
public StartMenuViewModel ViewModel { get { return (DataContext as StartMenuViewModel); } }
private void UserControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
ViewModel.Tile = tilePanel.ViewModel.TileModels;
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
return;
}
}
In my start screen ViewModel, I used ObservableCollection Tile
and use Tile.Add(tile); to populate my start screen with Tiles inside the TilePanel...
StartMenuViewModel.cs
TileModel tile = new TileModel() { Text = "Testing1", FontSize = 11, Size = TileSize.Medium, Background = (SolidColorBrush)new BrushConverter().ConvertFromString("#039BE5"), Tag="Something" };
tile.TileCommand = new RelayCommand(
p => Tile_TileClick(tile.Tag),
p => true
);
temp.Add(tile);
Now the problem is, if I add a new code below, tile = new TileModel() {...}
tile.TileCommand = new RelayCommand(...), even if I clicked on the first tile, my Tile_TileClick() will get the second tile's info (or the last tile inserted)...
Am I doing something wrong? Or Im doing everything wrong...?
This is not direct answer to your question, but hopefully it will give you few thoughts.
Ok, first of all, don't name your usercontrol like this:
<UserControl x:Class="MyNamespace.Tilev2" Name="Tile"/>
because the name can be easily overriden when using the usercontrol somewhere:
<local:Titlev2 Name="SomeOtherName" />
and the binding inside Tilevs with ElementName won't work: Command="{Binding ElementName=Tile, Path=TileClickCommand}"
Second, what's the point of Tilev2 usercontrol? Why don't just put the button directly to the DataTemplate inside TilePanel class?
If you need to reuse the template, you can put the template to resource dictionary.
If you need some special presentation code in the Tilev2 codebehind or you need to use the Tilev2 without viewmodel, it's better to create custom control instead of usercontrol in this case. it has much better design time support, and writing control templates it's easier (Triggers, DataTriggers, TempalteBinding, etc). If you used custom Control insead UserControl, you wouldn't have to write {Binding ElementName=Tile, Path=TileClickCommand}, or use RelativeSource, etc.
Third, it seems like you forced MVVM pattern where you can't really take advantage of it. Point of MVVM is separate application logic from presentation. But your Tile and TilePanel usercontrols are just presentation. You application logic could be in StartScreen which is concrete usage of TileName.
I would create custom controls called TilePanel (potentionally inherited from ItemsControl, Selector or ListBox) and if needed also for Tile. Both controls should not be aware of any viewmodels. There's absolutelly no need for that.
Take ListBox as an example. ListBox does not have viewmodel but can be easily used in MVVM scenarios. Just because ListBox it is not tied to any viewmodel, it can be databound to anything.
Just like ListBox creates ListBoxItems, or
Combobox creates ComboBoxItems, or
DataGrid creates DataGridRows or
GridView (in WinRT) creates GridViewRow, your TilePanel could create Tiles.
Bindings to tile specific properties, like Icon or Command could be specified in TilePanel.ItemContainerStyle orusing simillar appriach like DisplayMemberPath, resp ValueMemberPath in ListBox.
final usage could the look like:
<TilePanel ItemsSource="{Bidning ApplicationTiles}" />
or
<TilePanel>
<Tile Icon=".." Command=".." Text=".." />
<Tile Icon=".." Command=".." Text=".." />
</TilePanel>
Last, the name `TilePanel' evoked that it is some kind of panel like StackPanel, WrapPanel, etc. In other words, it is FrameworkElement inherited from Panel.
TilesView would be more suitable name for the control than TilePanel. The -View postfix is not from MVVM, it just follows naming convention -GridView, ListView...
Saw the problem...
To pass a parameter from button, I used CommandParameter so I could use it in switch-case scenario to know which button was clicked. But still, param was still null...
<Button x:Name="btnTile" Style="{StaticResource TileStyleButton}" CommandParameter="{Binding}" Command="{Binding Path=TileClickCommand, ElementName=Tile}" >
</Button>
TileCommand = new MyCommand() { CanExecuteFunc = param => CanExecuteCommand(), ExecuteFunc = param => Tile_TileClick(param)}
After 2 whole damn days, I changed it:
From this:
<UserControl Name="Tile"...>
<Button x:Name="btnTile" Style="{StaticResource TileStyleButton}" CommandParameter="{Binding Tag, ElementName=Tile}" Command="{Binding Path=TileClickCommand, ElementName=Tile}" >
</Button>
</UserControl>
To this:
<UserControl Name="Tile"...>
<Button x:Name="btnTile" Style="{StaticResource TileStyleButton}" CommandParameter="{Binding}" Command="{Binding Path=TileClickCommand, ElementName=Tile}" >
</Button>
</UserControl>
My first post does error because CommandParameter does not know where to get its DataContext so I replaced it to CommandParameter={Binding} so it will get whatever from the DataContext.
I'm trying to create TabItem Headers with Buttons that enable the User to close tabs. The visual representation and the Databinding of the object is just fine.
I've experimented with the DataContext, but so far I haven't found a workable solution.
My XAML:
<TabControl
Grid.Column="3"
Grid.Row="2"
x:Name="TabControlTargets"
ItemsSource="{Binding Path=ViewModelTarget.IpcConfig.DatabasesList, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding Path=ViewModelTarget.SelectedTab, UpdateSourceTrigger=PropertyChanged}">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock FontFamily="Calibri" FontSize="15" FontWeight="Bold" Foreground="{Binding FontColor}" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" Margin="0,0,20,0"/>
<Button HorizontalAlignment="Left" DataContext="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext}" Command="{Binding Path = ViewModelTarget.buttonRemoveDatabaseCommand}"
CommandParameter="**?**"
>
<Button.Content>
<Image Height="15" Width="15" Source="pack://application:,,,/Images/cancel.png" />
</Button.Content>
</Button>
</StackPanel>
</DataTemplate>
I have trouble figuring out how to set the CommandParameter of my button so that it refers to the correct object.
Here is my RelayCommand:
public ICommand buttonRemoveDatabaseCommand
{
get
{
if (_buttonRemoveDatabaseCommand == null)
{
_buttonRemoveDatabaseCommand = new RelayCommand(
param => RemoveDatabase(param)
);
}
return _buttonRemoveDatabaseCommand;
}
}
And here is my RemoveDatabase function:
public void RemoveDatabase(object dB)
{
this.IpcConfig.RemoveDataBase((PCDatabase)dB);
}
I would strongly prefer a solution that sticks to my "no code behind" approach.
As pointed in the comments, you can use CommandParameter="{Binding}" to pass the TabItem context to the command.
A better approach is though to move the command to the ViewModel of your TabItem.
Here an example implementation using Prism and Prism's EventAggregator. You can of course implement this with every other MVVM Framework or even implement it yourself, but that's up to you.
This would be your TabControl ViewModel, which contains a list of all databases or whatever it's meant to represent.
public class DatabasesViewModel : BindableBase
{
private readonly IEventAggregator eventAggregator;
public ObservableCollection<DatabaseViewModel> Databases { get; private set; }
public CompositeCommand CloseAllCommand { get; }
public DatabasesViewModel(IEventAggregator eventAggregator)
{
if (eventAggregator == null)
throw new ArgumentNullException(nameof(eventAggregator));
this.eventAggregator = eventAggregator;
// Composite Command to close all tabs at once
CloseAllCommand = new CompositeCommand();
Databases = new ObservableCollection<DatabaseViewModel>();
// Add a sample object to the collection
AddDatabase(new PcDatabase());
// Register to the CloseDatabaseEvent, which will be fired from the child ViewModels on close
this.eventAggregator
.GetEvent<CloseDatabaseEvent>()
.Subscribe(OnDatabaseClose);
}
private void AddDatabase(PcDatabase db)
{
// In reallity use the factory pattern to resolve the depencency of the ViewModel and assing the
// database to it
var viewModel = new DatabaseViewModel(eventAggregator)
{
Database = db
};
// Register to the close command of all TabItem ViewModels, so we can close then all with a single command
CloseAllCommand.RegisterCommand(viewModel.CloseCommand);
Databases.Add(viewModel);
}
// Called when the event is received
private void OnDatabaseClose(DatabaseViewModel databaseViewModel)
{
Databases.Remove(databaseViewModel);
}
}
Each tab would get one DatabaseViewModel as it's context. This is where the close command is defined.
public class DatabaseViewModel : BindableBase
{
private readonly IEventAggregator eventAggregator;
public DatabaseViewModel(IEventAggregator eventAggregator)
{
if (eventAggregator == null)
throw new ArgumentNullException(nameof(eventAggregator));
this.eventAggregator = eventAggregator;
CloseCommand = new DelegateCommand(Close);
}
public PcDatabase Database { get; set; }
public ICommand CloseCommand { get; }
private void Close()
{
// Send a refence to ourself
eventAggregator
.GetEvent<CloseDatabaseEvent>()
.Publish(this);
}
}
When you click the close Button on the TabItem, then CloseCommand would be called and send an event, that would notify all subscribers, that this tab should be closed. In the above example, the DatabasesViewModel listens to this event and will receive it, then can remove it from the ObservableCollection<DatabaseViewModel> collection.
To make the advantages of this way more obvious, I added an CloseAllCommand, which is a CompositeCommand that registers to each DatabaseViewModels CloseCommand as it's added to the Databases observable collection, which will call all registered commands, when called.
The CloseDatabaseEvent is a pretty simple and just a marker, that determines the type of payload it receives, which is DatabaseViewModel in this case.
public class CloseDatabaseEvent : PubSubEvent<DatabaseViewModel> { }
In real-world applications you want to avoid using the ViewModel (here DatabaseViewModel) as payload, as this cause tight coupling, that event aggregator pattern is meant to avoid.
In this case it's can be considered acceptable, as the DatabasesViewModel needs to know about the DatabaseViewModels, but if possible it's better to use an ID (Guid, int, string).
The advantage of this is, that you can also close your Tabs by other means (i.e. menu, ribbon or context menus), where you may not have a reference to the DatabasesViewModel data context.
I have a WinRT project based on prism with several pages, usercontrols etc.
For some reason I need to bind several views to a single model object accessed by the the viewmodels (each one belonging to a view).
The single model object is injected by a Unity container like other objects, that need to be singleton-like as the eventaggregator for instance.
To keep things simple I made an example with only one bool variable bound to a checkbox in each view, that should be synchronized over the views.
My problem is: When I check the box in the mainpage, the checkbox in a second page is following the value when navigating to that page(UserInpuPage in the example) BUT NOT the checkbox in the UserControl place on the Mainpage.
After a debug session I saw the variables in the single model having the right values, but the GUI on the Usercontrol (MyUserControl in the example) is not updated.
A Mechanism like GetBindingExpression(...) and then UpdateTarget() like in WPF seems not to exist in the WinRT library.
For design reasons (using prism mvvm I don't want to break the concept of the autowire and dynamical instantiation of the vm's) a static context defined in the resources section of the page and/or usercontrol is not what I'm looking for.
How can I achieve the update of the checkbox in the usercontrol with the model the same way as it works for the userinputpage after navigating?
Any help would be appreciated.
// Interface mendatory to work with Unitiy-Injection for RegisterInstance<ISingletonVM>(...)
public interface ISingletonVM
{
bool TestChecked{ get; set; }
}
public class SingletonVM : BindableBase, ISingletonVM
{
bool _testChecked = false;
public bool TestChecked
{
get
{
return _testChecked;
}
set
{
SetProperty(ref _testChecked, value);
}
}
}
This is the relevant code in the viewmodels (same for every vm, but vm from usercontrol in this case):
class MyUserControlViewModel : ViewModel
{
private readonly ISingletonVM _singletonVM;
public MyUserControlViewModel(ISingletonVM singletonVM)
{
_singletonVM = singletonVM;
}
public bool TestChecked
{
get
{
return _singletonVM.TestChecked;
}
set
{
_singletonVM.TestChecked = value;
}
}
}
Relevant XAML code fragments for the three views:
MainPage:
<prism:VisualStateAwarePage x:Name="pageRoot" x:Class="HelloWorldWithContainer.Views.MainPage"...>
...
<StackPanel Grid.Row="2" Orientation="Horizontal">
<ctrl:MyUserControl ></ctrl:MyUserControl>
<CheckBox IsChecked="{Binding TestChecked, Mode=TwoWay}" Content="CheckBox" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</StackPanel>
...
UserInputPage:
<prism:VisualStateAwarePage x:Name="pageRoot"
x:Class="HelloWorldWithContainer.Views.UserInputPage"
...
<CheckBox IsChecked="{Binding TestChecked, Mode=TwoWay}" Content="CheckBox" HorizontalAlignment="Left" Margin="440,190,0,0" VerticalAlignment="Top"/>
...
UserControl:
<UserControl
x:Class="HelloWorldWithContainer.Views.MyUserControl" prism:ViewModelLocator.AutoWireViewModel="True"
<Grid>
<CheckBox Content="CheckBox" IsChecked="{Binding TestChecked, Mode=TwoWay}" HorizontalAlignment="Left" VerticalAlignment="Top" Width="282"/>
</Grid>
Your user control never gets notified about changes in MyUserControlViewModel.TestChecked property and that's why the view is never updated. One thing you can do to fix this is to subscribe to your SingletonVM.PropertyChanged event in the constructor of MyUserControlViewModel. Your ISingletonVM needs to implement the INotifyPropertyChanged interface. So the constructor of MyUserControlViewModel will be something like this:
public MyUserControlViewModel(ISingletonVM singletonVM)
{
_singletonVM = singletonVM;
_singletonVM.PropertyChanged += (sender, args) => OnPropertyChanged("TestChecked");
}
I am binding a Collection at run time to a Combobox and I would like to set the Index after to 0. I could not find a straight answer to what I want.
_stationNames = new ObservableCollection<string>(_floorUnits.Unit.Select(f => f.Name));
_stationNames.Insert(0, "All");
stationsComboBox.ItemsSource = _stationNames;
stationsComboBox.SelectedIndex = 0;//Doesn;t work
Xaml
<ComboBox x:Name="stationsComboBox" Grid.Row="1" Grid.Column="1" Text="{Binding Name}"
SelectionChanged="StationComboBoxSelectionChanged" VerticalAlignment="Center" Margin="3"
SelectedIndex="0"/>
It sounds like you're trying to use it like you would with WinForms. WPF is a slightly different beast and a lot more powerful regarding bindings.
I recommend reading a bit on MVVM to get the most benefit from WPF. By binding the XAML to a view model class (rather than trying to wire things up in Code-behind) you will find you can accomplish what you want with a lot more flexibility without oodles of code.
For instance: Given the following VM:
public class MyViewModel: INotifyPropertyChanged
{
public ObservableCollection<string> StationNames
{
get;
private set;
}
public Something()
{
StationNames = new ObservableCollection<string>( new [] {_floorUnits.Unit.Select(f=>f.Name)});
StationNames.Insert(0, "All");
}
private string _selectedStationName = null;
public string SelectedStationName
{
get
{
return _selectedStationName;
}
set
{
_selectedStationName = value;
FirePropertyChanged("SelectedStationName");
}
}
private void FirePropertyChanged(string propertyName)
{
if ( PropertyChanged != null )
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
You can set your view's (XAML form) DataContext to an instance of the ViewModel and update your combo box definition to:
<ComboBox x:Name="stationsComboBox" Grid.Row="1" Grid.Column="1"
ItemsSource="{Binding Path=StationNames}" SelectedItem={Binding Path=SelectedStationName} VerticalAlignment="Center" Margin="3"
SelectedIndex="0"/>
From here whenever the combo box selection changes, the VM's SelectedStationName updates to reflect the current selection, and from anywhere in the VM code, setting the VM's SelectedStationName will update the combo's selection. (I.e. implementing a Reset button, etc.)
Normally though, with something like what you've suggested, I would be looking at binding directly to the Units collection. (or VM's derived from units if they themselves can be viewed/edited.) In any case it should give you a bit of a starting point to start researching into WPF bindings.