WPF数据模板和按钮

3
注意:我正在使用Visual Studio 2012。
我有以下DataTemplate(注意:其中包含一个TextBlock和Button)。
<DataTemplate DataType="{x:Type ctlDefs:myButton}">
    <StackPanel>
        <TextBlock Text="{Binding LabelText}" Margin="10,0,10,0"/>

        <Button Content="{Binding LabelText}" Margin="0,0,10,0" 
            Width="{Binding ButtonWidth}" 
            Height="35"
            Command="{Binding ButtonCommand}" 
            Visibility="Visible"                    
            />
    </StackPanel>
</DataTemplate>

我的屏幕正在动态地将控件myButton添加到一个Items Control中

 <ItemsControl ItemsSource="{Binding CommandButtons, Mode=OneWay}"
               FlowDirection="LeftToRight"
               DockPanel.Dock="Right"
              >

第一次渲染视图时,每个按钮都会显示 Textblock,但按钮本身不会显示。如果我隐藏视图,然后再次显示它,有时按钮会出现(如果 CommandButtons 列表没有更改,则会出现,如果更改了,则永远不会出现)。在渲染视图后,我查看了输出窗口,没有绑定错误。我错过了什么?

如果不绑定按钮的宽度,这种情况是否也会发生?ButtonWidth 实现了 INotifyPropertyChanged 吗? - LPL
是的。如果我移除ButtonWidth,它的功能仍然相同。从我使用WPF检查器所看到的情况来看,按钮看起来像是存在的(为其保留了空间),并且在第一次和第二次加载时所有属性都匹配,当它出现时。 - adondero
1个回答

4
我已经快速开发了一个应用程序来使用你的示例代码,并且没有出现视图方面的任何问题。下面是它运行时的样子。 Looks like this 如果有用的话,我在下面附上了我的代码。注意:出于简洁考虑,我没有添加真正的ICommand对象,并且我不小心将MyButton类命名为大写M而不是像你的示例一样是小写m。
ViewModelBase.cs
public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        var handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }
}

public class MainWindowViewModel : ViewModelBase
{
    public MainWindowViewModel()
    {
        this.CommandButtons = new ObservableCollection<MyButton>();
    }
    public ObservableCollection<MyButton> CommandButtons { get; private set; }
}

App.xaml.cs

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        var mainvm = new MainWindowViewModel();
        var window = new MainWindow
        {
            DataContext = mainvm
        };
        window.Show();

        mainvm.CommandButtons.Add(new MyButton { ButtonWidth = 50, LabelText = "Button 1" });
        mainvm.CommandButtons.Add(new MyButton { ButtonWidth = 50, LabelText = "Button 2" });
        mainvm.CommandButtons.Add(new MyButton { ButtonWidth = 50, LabelText = "Button 3" });
    }
}

MainWindow.xaml

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ctlDefs="clr-namespace:WpfApplication1"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <DataTemplate DataType="{x:Type ctlDefs:MyButton}">
        <StackPanel>
            <TextBlock Text="{Binding LabelText}" Margin="10,0,10,0"/>

            <Button Content="{Binding LabelText}" Margin="0,0,10,0" 
                    Width="{Binding ButtonWidth}" 
                    Height="35"
                    Command="{Binding ButtonCommand}" 
                    Visibility="Visible"                    
        />
        </StackPanel>
    </DataTemplate>
</Window.Resources>
<Grid>
    <ItemsControl ItemsSource="{Binding CommandButtons, Mode=OneWay}"
           FlowDirection="LeftToRight"
           DockPanel.Dock="Right"
          />
</Grid>

MyButton.cs

public class MyButton : ViewModelBase
{
    private string _labelText;

    public string LabelText
    {
        get { return this._labelText; }
        set { this._labelText = value; this.OnPropertyChanged("LabelText"); }
    }

    private double _buttonWidth;

    public double ButtonWidth
    {
        get { return _buttonWidth; }
        set { _buttonWidth = value; this.OnPropertyChanged("ButtonWidth"); }
    }

    private ICommand _buttonCommand;

    public ICommand ButtonCommand
    {
        get { return _buttonCommand; }
        set { _buttonCommand = value; this.OnPropertyChanged("ButtonCommand"); }
    }
}

我已经在我的机器上运行了你的代码,发现了一些差异:CommandButtons中使用了ObservableCollection而不是List;使用了ICommand而不是DelegateCommand。我进行了这些更改,但我的代码仍然无法正常工作。我认为还有其他问题存在。感谢您的帮助验证这种方法的可行性。我将尝试简化我的大型项目并查看结果。我接受这个答案,因为这就是它“应该”工作的方式。 - adondero
因为我的声望不够高,所以无法点击向上箭头 :-( - adondero
最终缩小范围。在我的样式文件中,有这个条目:<Style TargetType="{x:Type Button}"> <Setter Property="FocusVisualStyle" Value="{DynamicResource NuclearButtonFocusVisual}" /><Setter Property="Background" Value="{DynamicResource NormalBrush}" /> <Setter Property="Foreground" Value="{DynamicResource TextBrush}" /> <Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}" /> <!--<Setter Property="Template" Value="{DynamicResource ButtonTemplate}" />--> </Style>被注释掉的那一行是问题所在,但不确定为什么。 - adondero
很高兴你解决了它!你需要检查ButtonTemplate的定义,并查看是否包含了所有必需的PART定义。此外,委托命令是正确使用的,我通常在我的构造函数中挂钩它。this.Command = new DelegateCommand(o => this.MyMethod(); - failedprogramming

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