WPF привязка данных к свойству объекта - PullRequest
0 голосов
/ 11 января 2019

Я пытаюсь построить калькулятор, используя WPF. Я использую несколько страниц, чтобы я мог практиковать использование привязки данных между страницами. Тем не менее, мой дисплей не является обязательным. Когда я нажимаю «0», я хочу, чтобы на дисплее отображалось «0». Когда я набираю номер, дисплей не обновляется.

Я понимаю, что есть ряд похожих вопросов по SO. Разница между этими вопросами и моими заключается в том, что я уже внедряю INotifyPropertyChanged, но это не решило мою проблему.

Настройка кода

«NumberPad» содержит цифровые кнопки 0-9. «Дисплей» - это просто большая кнопка вверху, которая должна отображать число нажатых.

Calculator Screenshot

NumberPad.xaml.cs

public partial class NumberPad : Page
{
    Display m_display;

    public NumberPad()
    {
        InitializeComponent();
        m_display = new Display();
        this.DataContext = m_display;
    }

    private void Button0_Click(object sender, RoutedEventArgs e)
    {
        m_display.EquationText = "0";
    }

    private void Button1_Click(object sender, RoutedEventArgs e)
    {
        m_display.EquationText = "1";
    }
 }

Display.xaml.cs

public partial class Display : Page, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private string m_equationText;

    public Display()
    {
        InitializeComponent();
        EquationText = "Hi";
    }

    public string EquationText
    {
        get { return m_equationText; }
        set
        {
            m_equationText = value;
            OnPropertyChanged("EquationText");
        }
    }

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Display.xaml

<Page x:Class="WPFCalculator.Display"
    x:Name="_this"
    ....
    Title="Display">

    <Button Content="{Binding ElementName=_this, Path=EquationText, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource buttonStyle}" FontSize="60">

    </Button>
</Page>

Ожидается: я должен быть в состоянии нажать 0 или 1, и соответствующий номер отобразится на кнопке дисплея. Фактически: только «Привет» отображается во время выполнения.

Важное замечание о Display.xaml

Я добавил в "ElementName = _this, Path =" для отображения "Привет". Если бы у меня был только «Binding EquationText», он не отображал бы «Привет» или 0, 1 или что-то еще. Я включил этот код, потому что это единственный способ, которым я могу заставить дисплей показывать что-либо в настоящее время

Заключение

Несмотря на это, я считаю, что DataContext устанавливается на свойство EquationText объекта, отличное от того, что на самом деле связывается. Как я могу привязать отображение к свойству таким образом, чтобы NumberPad мог обновить его?

Спасибо!

Ответы [ 3 ]

0 голосов
/ 11 января 2019

Позвольте мне сначала попытаться помочь вам найти решение вашей проблемы:

Нет необходимости определять:

<Button Content="{Binding ElementName = _this, ....}"></Button>

потому что, когда вы устанавливаете новый созданный экземпляр отображения в своем файле кода для свойства DataContext вашего фактического экземпляра окна (this.DataContext), для WPF становится ясно, что данные, которые вы хотите предоставить в окно, лежат в вашем окне. Экземпляр отображения установлен в свойство DataContext. Когда мы используем DataBinding, WPF иерархически ищет в дереве элементов следующий элемент, имеющий DataContext. Это означает, что, поскольку у вашей кнопки нет DataContext, WPF поднимается на уровень и смотрит (поскольку у вас нет иерархических элементов управления над кнопкой), установлен ли DataContext из окна. Поскольку DataContext в вашем случае уже был задан в вашем коде файла, WPF теперь знает, что вы хотите привязать данные к DataContext окна. Ну, все, что вам нужно сделать, это привязать к свойству, которое вы хотите отобразить. Вы можете сделать это так:

<Button Content="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
 AncestorType=Display}, Path=EquationText}"...>

Я бы порекомендовал вам использовать MVVM Pattern, чтобы получить достойное решение для упражнения, которое вы пытаетесь решить (:!

Код, который вы пытаетесь написать, смешивает разные обязанности, которые необходимо четко разделить. Окно, как показано на рисунке выше, должно представлять вид. Это означает, что не обязательно, чтобы окно отображения было отдельной страницей. Было бы лучше использовать контейнер, такой как сетка, для структурирования представления. Кроме того, не должно быть никакой логики в коде позади файла к файлу xaml. Вместо использования ручек нажатия кнопок, было бы лучше использовать команды, определенные во ViewModel. Идеальное состояние, которого вы хотите достичь благодаря MVVM, - это то, что вы хотите полностью отделить представление и модель друг от друга. Это делает ваш код более удобным и легко заменяемым. Чтобы вернуться к указателям на клики: Не рекомендуется создавать обратный вызов для каждого отдельного номера. Это делает код более запутанным, особенно это приводит к избыточности, поскольку при каждом обратном вызове происходит одно и то же. Вы присваиваете строку тому же свойству. Таким образом, у вас будет 9 ключей, 9 обратных вызовов и, следовательно, 9x одинакового кода.

Я бы посоветовал вам взглянуть на шаблон MVVM, и я убежден, что ваш код будет более понятным.

0 голосов
/ 11 января 2019

Добро пожаловать в S / O, отличный бесплатный источник, чтобы найти и предложить помощь. Хотя существует множество статей о создании шаблонов MVVM и изучении привязок, иногда необходимо иметь собственное мышление, прежде чем что-то «щелкнет» к этому моменту. Ваш визуальный образец - это совершенно прекрасная и известная цель. Я создал окно WPF в аналогичном контексте. Однако я создал один метод для принятия всех кнопок, поэтому мне не приходилось многократно создавать одну функцию для каждого вызова. Глядя на отправителя (входящий объект кнопки) и получая его в виде «кнопки», и просматривая его свойство Content, я получаю соответствующие 0, 1, 2 и +, -, *, / по мере необходимости. Для чисел я просто добавляю на дисплей, для знаков и равенства я оставляю это на ваше усмотрение. Однако, поскольку значение связано с публичным свойством (через get / set), это то, что «привязано» к тексту, и вы увидите, что оно обновляется.

Теперь вашу логику для математических функций, добавляемых к вашему внутреннему свойству "_equationText", вы, очевидно, включите, очистите отображение между функциями +, -, *, / по мере необходимости и возобновите обучение. НТН. Я создал свое окно с именем MyCalculator, и вы можете скопировать / вставить и запустить его оттуда.

MyCalculator.xaml

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="60" />
            <ColumnDefinition Width="60" />
            <ColumnDefinition Width="60" />
            <ColumnDefinition Width="60" />
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <RowDefinition Height="60" />
            <RowDefinition Height="60" />
            <RowDefinition Height="60" />
            <RowDefinition Height="60" />
            <RowDefinition Height="60" />
            <RowDefinition Height="60" />
            <RowDefinition Height="60" />
            <RowDefinition Height="60" />
        </Grid.RowDefinitions>


        <TextBox Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="4" 
            IsReadOnly="True"
            Text="{Binding DisplayText}"
            Height="28" FontSize="16"
            TextAlignment="Right"
            VerticalAlignment="Center"/>


        <Button Content="7" Grid.Row="2" Grid.Column="0"
            Click="AnyButton_Click" />

        <Button Content="8" Grid.Row="2" Grid.Column="1" 
            Click="AnyButton_Click" />

        <Button Content="9" Grid.Row="2" Grid.Column="2" 
            Click="AnyButton_Click" />

        <Button Content="+" Grid.Row="2" Grid.Column="3" 
            Click="AnyButton_Click" />


        <Button Content="4" Grid.Row="3" Grid.Column="0"
            Click="AnyButton_Click" />

        <Button Content="5" Grid.Row="3" Grid.Column="1" 
            Click="AnyButton_Click" />

        <Button Content="6" Grid.Row="3" Grid.Column="2" 
            Click="AnyButton_Click" />

        <Button Content="-" Grid.Row="3" Grid.Column="3" 
            Click="AnyButton_Click" />


        <Button Content="1" Grid.Row="4" Grid.Column="0"
            Click="AnyButton_Click" />

        <Button Content="2" Grid.Row="4" Grid.Column="1" 
            Click="AnyButton_Click" />

        <Button Content="3" Grid.Row="4" Grid.Column="2" 
            Click="AnyButton_Click" />

        <Button Content="*" Grid.Row="4" Grid.Column="3" 
            Click="AnyButton_Click" />


        <!--<Button Content="1" Grid.Row="5" Grid.Column="0"
            Click="AnyButton_Click" />-->

        <Button Content="0" Grid.Row="5" Grid.Column="1" 
            Click="AnyButton_Click" />

        <!--<Button Content="3" Grid.Row="5" Grid.Column="2" 
            Click="AnyButton_Click" />-->

        <Button Content="/" Grid.Row="5" Grid.Column="3" 
            Click="AnyButton_Click" />


        <Button Content="=" Grid.Row="6" Grid.Column="3" 
            Click="AnyButton_Click" />
    </Grid>
</Window>

MyCalculator.xaml.cs

using System.ComponentModel;
using System.Windows;

namespace StackHelp
{
    /// <summary>
    /// Interaction logic for MyCalculator.xaml
    /// </summary>
    public partial class MyCalculator : Window, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public MyCalculator()
        {
            DataContext = this;
            InitializeComponent();
        }

        private void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private string m_equationText;

        private string _displayText = "Hi";
        public string DisplayText
        { get { return _displayText; }
            set { _displayText = value;
                    OnPropertyChanged( "DisplayText" );
            }
        }


        private void AnyButton_Click(object sender, RoutedEventArgs e)
        {
            var whichBtn = sender as System.Windows.Controls.Button;
            if (whichBtn == null)
                return;

            // pre-clear just because it was sample anyhow.
            if (_displayText == "Hi")
                _displayText = "";

            switch( whichBtn.Content )
            {
                case "0":
                case "1":
                case "2":
                case "3":
                case "4":
                case "5":
                case "6":
                case "7":
                case "8":
                case "9":
                    DisplayText += whichBtn.Content;
                    break;

                case "+":
                case "-":
                case "*":
                case "/":
                case "=":
                    // how to handle...  using your computation learning method
                    // then finish by setting the display text with the new string 
                    // represented answer
                    break;

                default:
                    // invalid button
                    break;
            }

        }
    }
}
0 голосов
/ 11 января 2019

Я не уверен на 100%, что происходит без всего кода, но попробуйте это:

<Button Content="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
 AncestorType=Display}, Path=EquationText}" Style="{StaticResource buttonStyle}" FontSize="60">
...