wpf Caliburn Micro、绑定和通知

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13011406/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 05:53:26  来源:igfitidea点击:

Caliburn Micro, Binding and Notifying

wpfbindingcaliburn.micro

提问by EluciusFTW

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:

经过几个月的 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); }
    }
}

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.

所以这里是:我使用绑定约定,即 MainView 中的 Property 应该绑定到 View 中的同名元素。

  • First of all, notice that PlayerListis 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 ToStringmethod in the PlayerProfile class. Why? This seems clumsy... (The staticInfos.Load()method populates the PlayerProfileswith 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 CanAddPlayerProperty, the button never is enabled, not if I start typing, nor when the textbox looses focus with text in it. Why?

  • 首先,请注意这PlayerList只是PlayerProfiles我创建的球员姓名列表,因为当绑定到 a 时Combobox,当我命名时他们不会出现Combobox PlayerProfiles,但是当我通过列表并命名时他们会出现PlayerList- 虽然我已经覆盖ToStringPlayerProfile class. 为什么?这看起来很笨拙......(该staticInfos.Load()方法填充了PlayerProfiles几个虚拟名称,如果你想知道......)

  • 当我在文本框中键入内容(称为TB_AddPlayer)并按下按钮(称为AddPlayer)时,会出现测试字符串,因此我知道操作已发生,但新名称未出现在组合框中

  • 此外,如果我在CanAddPlayer属性中使用注释行,则按钮永远不会启用,如果我开始输入,也不会在文本框失去焦点时启用其中的文本。为什么?

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!

抱歉,对于这些基本问题,我已经盯着“Hello World”caliburn 示例看了好几个小时,但没有看到我遗漏了什么......谢谢。提前!

Edit: Here's the .xaml

编辑:这是 .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>

采纳答案by devdigital

Firstly, I would start by removing your PlayerListproperty, and update your PlayerProfilesproperty like so:

首先,我将首先删除您的PlayerList财产,然后PlayerProfiles像这样更新您的财产:

 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_AddPlayerto just AddPlayer(or better still something like PlayerNameas that's what it is).

接下来,我不会将您的视图模型属性称为描述它们如何呈现的东西,即更改TB_AddPlayer为 just AddPlayer(或者更好PlayerName的是,它仍然是这样的)。

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:

接下来,如果您调用 ComboBox PlayerProfiles,则绑定应通过约定自动发生。在您的 AddPlayer 方法中,您希望通过您的属性而不是支持字段添加新的玩家配置文件:

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.

因为 PlayerProfiles 是一个 BindableCollection,将执行更改通知,并且您的 ComboBox 将被更新。

Also, I would always explicitly specify the access modifier for your backing fields, i.e. private string _playerName;not just string _playerName;

此外,我总是会为您的支持字段明确指定访问修饰符,即private string _playerName;不仅仅是字符串_playerName;

Try these changes, and update your code in the question if you still have problems.

尝试这些更改,如果仍有问题,请更新问题中的代码。

回答by Chen Kinnrot

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.

在您的文本框中使用 updatesourcetrigger=property changed The Can Execute is not updated 因为您没有使用依赖(或依赖项不记得名称)属性。

The combobox not updating cause you should bind it to a bindable collection.

组合框未更新导致您应该将其绑定到可绑定集合。