Caliburn Micro,绑定和通知

5

在亲身体验了几个月的WPF之后,我决定尝试一下Caliburn Micro。但是我遇到了几个问题无法解决。以下是我的代码和问题:

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); }
    }
}

所以,我使用绑定约定,即 MainView 中的属性应该绑定到 View 中同名的元素。
首先,注意 PlayerList 只是 PlayerProfiles 中玩家名称列表,我创建它是因为当绑定到 Combobox 时,如果我将 Combobox 命名为 PlayerProfiles,则它们不会显示出来,但是如果我通过列表并将其命名为 PlayerList,则它们会显示出来——尽管我已经在 PlayerProfile 类中重写了 ToString 方法。为什么?这似乎很笨拙...(如果你想知道,staticInfos.Load() 方法会将 PlayerProfiles 填充为几个虚拟名称...)
当我在文本框(称为 TB_AddPlayer)中键入内容并按下按钮(称为 AddPlayer)时,测试字符串会出现,所以我知道操作已经发生,但是新名称不会出现在组合框中。
此外,如果我使用 CanAddPlayer 属性中的注释行,则按钮永远不会启用,无论我何时开始输入,还是当文本框失去焦点时有文本。为什么?
抱歉问这些基础问题,我已经盯着“Hello World”caliburn示例看了几个小时,但不知道我错过了什么...提前致谢!
编辑:这是 .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>

请问您能否发布您的 MainView XAML 代码? - nemesv
2个回答

4
首先,我建议删除您的PlayerList属性,并将PlayerProfiles属性更新如下:
 public BindableCollection<PlayerProfile> PlayerProfiles {
    get { return _playerProfiles; }
    set { 
        _playerProfiles = value;
        NotifyOfPropertyChange(() => PlayerProfiles);
    }
}

接下来,我不会将您的视图模型属性称为描述它们如何呈现的名称,即将 TB_AddPlayer 更改为仅 AddPlayer (或更好的是像 PlayerName 这样的名称,因为这就是它的作用)。
其次,如果将您的组合框命名为 PlayerProfiles,则绑定应通过约定自动完成。在您的 AddPlayer 方法中,您需要通过属性添加新的球员个人资料,而不是后备字段:
更改为:
public void AddPlayer() {
     _playerProfiles.Add(new PlayerProfile(TB_AddPlayer));
     Test = "Pressed";
     NotifyOfPropertyChange(() => PlayerProfiles);
}

to:

public void AddPlayer() {
     this.PlayerProfiles.Add(new PlayerProfile(this.PlayerName));
}

因为PlayerProfiles是一个BindableCollection,所以会执行更改通知,并且您的ComboBox将会更新。
此外,我建议始终明确指定后备字段的访问修饰符,例如private string _playerName;而不仅仅是string _playerName; 尝试这些更改,并在您仍然有问题时更新您的代码。

1
谢谢你的回答!但是,当我直接绑定到“PlayerProfiles”时(并且我已经在通知中进行了更改),组合框不会显示名称,而是两次(Load()方法生成了两个配置文件)输入“无法找到'Project_MVVM.Models.PlayerProfile'的视图”。这是什么意思? - EluciusFTW
1
另外,为了确保,我认为如果我写 'string a...' 和 'private string a...',它们会以完全相同的方式编译,即省略前缀会自动将其设置为 private,或者我错了吗? - EluciusFTW
1
你是正确的,但最好的做法是显式地添加“private”访问修饰符。关于你的第一个问题,Caliburn.Micro默认会假定集合中的类型是视图模型,并尝试查找相关的视图(例如PlayerProfile.xaml),并将你的PlayerProfile类型绑定到PlayerProfile视图)。然而,由于这不是视图模型类型,你可以将ComboBox的“DisplayMemberPath”属性设置为PlayerProfile类型的属性,或者创建一个ComboBox ItemTemplate。 - devdigital
1
请参考https://dev59.com/bUfRa4cB1Zd3GeqP-6Z3,了解ItemTemplate的示例。如果您想在ComboBox中显示更多内容而不仅仅是字符串,则可以使用它。 - devdigital
1
所以Caliburn在显示对象时默认不使用ToString方法,这是个好消息!我猜除了模板之外,我还可以创建一个新的.xaml文件,将其作为PlayerProfile模型的视图? - EluciusFTW
1
是的,如果您有一些复杂的显示需求,那么您可以这样做。但在这种情况下,更有意义的做法是将您的PlayerProfile作为视图模型类型(即PlayerProfileViewModel),您可以从PlayerProfiles集合中创建它(例如使用LINQ投影)。 - devdigital

1

我想我已经得到了答案

在您的文本框中使用updatesourcetrigger=property changed属性。Can Execute没有更新,因为您没有使用depends on(或dependencies不记得名称)属性。

组合框未更新,因为您应该将其绑定到可绑定集合。


1
默认情况下,使用Caliburn.Micro时,所有文本框实例的更新源触发器被设置为属性更改。 - devdigital
1
仍然,我无法使那一部分工作。现在当按下按钮时,我正在将新玩家添加到Combobox中,但无论文本框的内容是什么,该玩家都没有名称(即,在Combobox选择中出现了更多的白线)...为什么? - EluciusFTW
1
有人有想法吗?文本框绑定仍然无法工作 :-( ... 所有的情况仍然和上一条评论一样。 - EluciusFTW

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接