After a couple of months of hands-on WPF I decided to give Caliburn Micro a shot. I have several things I can't get to work. I'll post the code first, see my questions/problems below:
public class MainViewModel : PropertyChangedBase
{
BindableCollection<PlayerProfile> _playerProfiles = staticInfos.Load();
public BindableCollection<PlayerProfile> PlayerProfiles {
get { return _playerProfiles; }
set {
_playerProfiles = value;
NotifyOfPropertyChange(() => PlayerList);
}
}
string _tb_AddPlayer;
public string TB_AddPlayer {
get { return _tb_AddPlayer; }
set {
_tb_AddPlayer = value;
NotifyOfPropertyChange(() => TB_AddPlayer);
NotifyOfPropertyChange(() => CanAddPlayer);
}
}
List<string> _pl = new List<string>();
public List<string> PlayerList {
get{
foreach (PlayerProfile p in PlayerProfiles) {
if (!_pl.Contains(p.Name)) _pl.Add(p.Name);
}
return _pl;
}
set {
_pl = value;
NotifyOfPropertyChange(()=>PlayerList);
}
}
// Dummy Test String
string _test;
public string Test {
get { return _test; }
set {
_test = value;
NotifyOfPropertyChange(() => Test);
}
}
// Button Action
public void AddPlayer() {
_playerProfiles.Add(new PlayerProfile(TB_AddPlayer));
Test = "Pressed";
NotifyOfPropertyChange(() => PlayerProfiles);
}
public bool CanAddPlayer {
get { return true; }
// doesnt work: get { return !string.IsNullOrWhiteSpace(TB_AddPlayer); }
}
}
So here goes: I use the binding convention, i.e., a Property in the MainView should bind to the Element of same Name in the View.
First of all, notice that PlayerList
is just a list of the names of the Players in PlayerProfiles
, which I created because when binding to a Combobox
, they don't show up when I name the Combobox PlayerProfiles
, yet they do when I go via the list and name it PlayerList
- although I have overridden the ToString
method in the PlayerProfile class
. Why? This seems clumsy... (The staticInfos.Load()
method populates the PlayerProfiles
with several dummy names, if you are wondering...)
When I type something in the Textbox (called TB_AddPlayer
) and press the button (called AddPlayer
) the Test string appears, so I know the action took place, but the new name does not appear in the combobox
Also, If I use the commented line in the CanAddPlayer
Property, the button never is enabled, not if I start typing, nor when the textbox looses focus with text in it. Why?
Sorry for these basic questions, I have stared at the 'Hello World' caliburn example for hours and don't see what I am missing... Thx. in advance!
Edit: Here's the .xaml
<UserControl x:Class="MixGameM8_MVVM.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MixGameM8_MVVM.Views">
<UserControl.Resources>
<Style TargetType="TextBlock" x:Key="TB_A">
<Setter Property="Margin" Value="5,5,5,5" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="FontSize" Value="15" />
</Style>
<!-- ... Some more of this kind -->
</UserControl.Resources>
<Grid>
<!-- ... Grid definitions -->
<Border Style="{StaticResource Border_A}" Grid.Row="0" Grid.Column="0">
<StackPanel Name="SP_Controls">
<TextBlock Style="{StaticResource TB_A}" Text="Controls"/>
<ComboBox Style="{StaticResource CB_A}" Name="PlayerProfiles"/>
<StackPanel Orientation="Horizontal">
<TextBox Style="{StaticResource TBox_A}" Name="TB_AddPlayer" MinWidth ="100" />
<Button Style="{StaticResource Button_AddPlayer}" Content="Add Player" Name="AddPlayer" />
</StackPanel>
</StackPanel>
</Border>
<!-- ... And some more cells in the grid, one containing the dummy textbox -->
</Grid>
</UserControl>
Firstly, I would start by removing your PlayerList
property, and update your PlayerProfiles
property like so:
public BindableCollection<PlayerProfile> PlayerProfiles {
get { return _playerProfiles; }
set {
_playerProfiles = value;
NotifyOfPropertyChange(() => PlayerProfiles);
}
}
Next, I would not call your view model properties something which describes how they are rendered, i.e. change TB_AddPlayer
to just AddPlayer
(or better still something like PlayerName
as that's what it is).
Next, if you call your ComboBox PlayerProfiles
, then the binding should automatically happen via convention. In your AddPlayer method, you want to add the new player profile via your property, not the backing field:
Change:
public void AddPlayer() {
_playerProfiles.Add(new PlayerProfile(TB_AddPlayer));
Test = "Pressed";
NotifyOfPropertyChange(() => PlayerProfiles);
}
to:
public void AddPlayer() {
this.PlayerProfiles.Add(new PlayerProfile(this.PlayerName));
}
Because PlayerProfiles is a BindableCollection, change notifications will be performed, and your ComboBox will be updated.
Also, I would always explicitly specify the access modifier for your backing fields, i.e. private string _playerName;
not just string _playerName;
Try these changes, and update your code in the question if you still have problems.
I think I got the answers
On your text box use updatesourcetrigger=property changed The Can Execute is not updating cause you didn't use the depends on (or dependencies dont remeber the name) attribute.
The combobox not updating cause you should bind it to a bindable collection.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With