Шаблоны данных WPF - PullRequest
       11

Шаблоны данных WPF

0 голосов
/ 24 апреля 2010

Я начинаю работать с WPF и пытаюсь разобраться с подключением данных к пользовательскому интерфейсу. Мне удалось подключиться к классу без каких-либо проблем, но я действительно хочу подключиться к свойству главного окна.

Вот XAML:

<Window x:Class="test3.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom="clr-namespace:test3"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <CollectionViewSource
        Source="{Binding Source={x:Static Application.Current}, Path=Platforms}"
        x:Key="platforms"/>
    <DataTemplate DataType="{x:Type custom:Platform}">
        <StackPanel>
            <CheckBox IsChecked="{Binding Path=Selected}"/>
            <TextBlock Text="{Binding Path=Name}"/>
        </StackPanel>
    </DataTemplate>
</Window.Resources>
<Grid>
    <ListBox ItemsSource="{Binding Source={StaticResource platforms}}"/>
</Grid>

Вот код для главного окна:

public partial class MainWindow : Window
{
    ObservableCollection<Platform> m_platforms;

    public MainWindow()
    {
        m_platforms = new ObservableCollection<Platform>();

        m_platforms.Add(new Platform("PC"));

        InitializeComponent();
    }

    public ObservableCollection<Platform> Platforms
    {
        get { return m_platforms; }
        set { m_platforms = value; }
    }
}

Вот класс Platform:

public class Platform
{
    private string m_name;
    private bool m_selected;

    public Platform(string name)
    {
        m_name = name;
        m_selected = false;
    }

    public string Name
    {
        get { return m_name; }
        set { m_name = value; }
    }

    public bool Selected
    {
        get { return m_selected; }
        set { m_selected = value; }
    }
}

Это все компилируется и работает нормально, но в окне списка ничего не отображается. Если я поставлю точку останова на метод get платформ, он не будет вызван. Я не понимаю, поскольку платформы должны подключаться к XAML!

Ответы [ 5 ]

2 голосов
/ 24 апреля 2010

Ваш код выглядит нормально, если не считать того, что Binding на Source на CollectionViewSource неверен. Вы, вероятно, имели в виду это:

 <CollectionViewSource
    Source="{Binding Source={x:Static Application.Current}, Path=MainWindow.Platforms}"
    x:Key="platforms"/>

Без этого изменения Binding фактически искал свойство Platforms в Application экземпляре.

1 голос
/ 25 апреля 2010

Несколько более подходящее решение - дать вашему окну имя.Хорошее соглашение это _this.

<Window x:Name="_this" x:Class="test3.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom="clr-namespace:test3"
    Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <CollectionViewSource
            Source="{Binding ElementName=_this, Path=Platforms}"
            x:Key="platforms"/>
        <DataTemplate DataType="{x:Type custom:Platform}">
            <StackPanel>
                <CheckBox IsChecked="{Binding Path=Selected}"/>
                <TextBlock Text="{Binding Path=Name}"/>
            </StackPanel>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <ListBox ItemsSource="{Binding Source={StaticResource platforms}}"/>
    </Grid>
1 голос
/ 24 апреля 2010

Я бы предложил вам добавить платформы не в MainWindow, а вместо этого установить его как DataContext MainWindow (обернутый внутри ViewModel).

Таким образом, вы можете очень легко связываться с ним (код привязки будет выглядеть как ItemsSource = {Binding Path = Platforms}).

Это часть дизайна WPF, в которой каждая форма должна иметь явный DataContext, к которому она привязана.

0 голосов
/ 25 апреля 2010

Используя DataContext, это становится еще проще!

<Window x:Class="test5.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom="clr-namespace:test5"
    Title="MainWindow" Height="190" Width="177">
    <Window.Resources>
        <CollectionViewSource
            Source="{Binding Path=.}"
            x:Key="platforms"/>
        <DataTemplate x:Key="platformTemplate" DataType="{x:Type custom:Platform}">
            <StackPanel Orientation="Horizontal">
                <CheckBox Margin="1" IsChecked="{Binding Path=Selected}"/>
                <TextBlock Margin="1" Text="{Binding Path=Name}"/>
            </StackPanel>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="23" />
            <RowDefinition Height="23" />
        </Grid.RowDefinitions>
        <ListBox Grid.Row="0"
            ItemsSource="{Binding Source={StaticResource platforms}}" 
            ItemTemplate="{StaticResource platformTemplate}"/>
        <Button Click="OnBuild" Grid.Row="1">Build...</Button>
        <Button Click="OnTogglePC" Grid.Row="2">Toggle PC</Button>
    </Grid>
</Window>

Вот код, стоящий за этим:

private ObservableCollection<Platform> m_platforms;

public MainWindow()
{
    InitializeComponent();

    m_platforms = new ObservableCollection<Platform>();

    m_platforms.Add(new Platform("PC"));
    m_platforms.Add(new Platform("PS3"));
    m_platforms.Add(new Platform("Xbox 360"));

    DataContext = m_platforms;
}

public void OnBuild(object sender, RoutedEventArgs e)
{
    string text = "";

    foreach (Platform platform in m_platforms)
    {
        if (platform.Selected)
        {
            text += platform.Name + " ";
        }
    }

    if (text == "")
    {
        text = "none";
    }

    MessageBox.Show(text, "WPF TEST");
}

public void OnTogglePC(object sender, RoutedEventArgs e)
{
    m_platforms[0].Selected = !m_platforms[0].Selected;
}

Обратите внимание, что я отменил необходимость объявлять Platforms как свойство главного окна, вместо этого я назначаю его DataContext, и источником XAML становится просто "."

0 голосов
/ 25 апреля 2010

Вот мой обновленный код, XAML:

<Window x:Name="_this"
    x:Class="test3.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom="clr-namespace:test3"
    Title="MainWindow" Height="190" Width="177">
    <Window.Resources>
        <CollectionViewSource
            Source="{Binding ElementName=_this, Path=Platforms}"
            x:Key="platforms"/>
        <DataTemplate x:Key="platformTemplate" DataType="{x:Type custom:Platform}">
            <StackPanel Orientation="Horizontal">
                <CheckBox Margin="1" IsChecked="{Binding Path=Selected}"/>
                <TextBlock Margin="1" Text="{Binding Path=Name}"/>
            </StackPanel>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="23" />
            <RowDefinition Height="23" />
        </Grid.RowDefinitions>
        <ListBox Grid.Row="0"
            ItemsSource="{Binding Source={StaticResource platforms}}" 
            ItemTemplate="{StaticResource platformTemplate}"/>
        <Button Click="OnBuild" Grid.Row="1">Build...</Button>
        <Button Click="OnTogglePC" Grid.Row="2">Toggle PC</Button>
    </Grid>
</Window>

Код XAML:

public partial class MainWindow : Window
{
    ObservableCollection<Platform> m_platforms;

    public MainWindow()
    {
        m_platforms = new ObservableCollection<Platform>();

        m_platforms.Add(new Platform("PC"));
        m_platforms.Add(new Platform("PS3"));
        m_platforms.Add(new Platform("Xbox 360"));

        InitializeComponent();
    }

    public ObservableCollection<Platform> Platforms
    {
        get { return m_platforms; }
        set { m_platforms = value; }
    }

    private void OnBuild(object sender, RoutedEventArgs e)
    {
        string text = "";

        foreach (Platform platform in m_platforms)
        {
            if (platform.Selected)
            {
                text += platform.Name + " ";
            }
        }

        if (text == "")
        {
            text = "none";
        }

        MessageBox.Show(text, "WPF TEST");
    }

    private void OnTogglePC(object sender, RoutedEventArgs e)
    {
        m_platforms[0].Selected = !m_platforms[0].Selected;
    }
}

... и, наконец, код платформы, улучшенный для завершения двустороннего взаимодействия:

public class Platform : INotifyPropertyChanged
{
    private string m_name;
    private bool m_selected;

    public Platform(string name)
    {
        m_name = name;
        m_selected = false;
    }

    public string Name
    {
        get { return m_name; }
        set
        {
            m_name = value;

            OnPropertyChanged("Name");
        }
    }

    public bool Selected
    {
        get { return m_selected; }
        set
        {
            m_selected = value;

            OnPropertyChanged("Selected");
        }
    }

    private void OnPropertyChanged(string name)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}
...