1

我刚刚开始使用 WPF,但仍然没有清楚地了解它的所有事实。

现在我正在搞乱数据绑定和线程来更新数据并按时间表示它。我有一个带有 2 个文本框的主窗口和一个用一个线程定义的用户控件,当我单击它时该线程开始运行。在这个线程中,我调用了在我绑定到文本框的主窗口中设置的 2 个属性,一个通过代码,另一个通过 xaml。

问题是代码生成的数据绑定有效,而 xaml 生成的数据绑定仅在我直接更新文本框时才有效。我一直想知道为什么,并通过一些更改使其工作,但我怀疑这个解决方案是“正确的”。

代码是这样的

主窗口.xaml

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded" xmlns:my="clr-namespace:WpfApplication1.Elements">
    <Grid>
        <TextBox Name="Textbox1" Height="23" HorizontalAlignment="Left" Margin="188,50,0,0" VerticalAlignment="Top" Width="120" Background="White" Text="{Binding Source=my:MainWindow, Path=datavalue}" />
        <TextBox Name="textBox2" Background="White" Height="23" HorizontalAlignment="Left" Margin="188,101,0,0"  Text="{Binding Path=datavalue2, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}} }" VerticalAlignment="Top" Width="120" />

        <Label Content="Label" Height="28" HorizontalAlignment="Left" Margin="202,152,0,0" Name="label1" VerticalAlignment="Top" />
        <my:Test Margin="81,138,346,92" x:Name="test1" />
    </Grid>
</Window>

主窗口.xaml.cs

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private int MydataValue;

        public int datavalue
        {
            get
            {
                return MydataValue;
            }
            set
            {
                MydataValue = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this,
                        new PropertyChangedEventArgs(
                        "datavalue"));
                }
             }   
        }

        private int mydatavalue2;

        public int datavalue2
        {
            get
            {
                return mydatavalue2;
            }
            set{
                mydatavalue2 = value;
            }
        }



        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            datavalue2 = 10;
            datavalue = 15;
            Binding bind = new Binding();
            bind.Source = this;
            bind.Mode = BindingMode.TwoWay;
            bind.Path = new PropertyPath("datavalue");
            Textbox1.SetBinding(TextBox.TextProperty, bind);
        } 
    }

}

这是用户控件

测试.xaml.cs

namespace WpfApplication1.Elements
{
    /// <summary>
    /// Interaction logic for Test.xaml
    /// </summary>
    public partial class Test : UserControl
    {
        private Thread t;
        private int resolution;
        private Stopwatch sw,sw2;
        private delegate void changeIconDelegate(long ib);
        private ImageBrush b;
        private Boolean end=false;
        private int count = 0;

        public Test()
        {
            InitializeComponent();
            sw = new Stopwatch();
            sw2 = new Stopwatch();
            b = new ImageBrush();
            resolution = 100;
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            b.ImageSource = new BitmapImage(new Uri("pack://application:,,,/DataBinding;component/Images/249137482.png", UriKind.Absolute));
            this.Background = b;
        }

        private void UserControl_MouseUp(object sender, MouseButtonEventArgs e)
        {
            t = new Thread(new ThreadStart(ThreadTask));
            t.Start();
        }

        private void ThreadTask()
        {
            while (!end)
            {
                count = count + 1;
                Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)delegate()
                {
                    Application.Current.Windows.OfType<MainWindow>().First().datavalue = count;
                    Application.Current.Windows.OfType<MainWindow>().First().datavalue2 = count;
                });

                Thread.Sleep((resolution - sw.Elapsed.TotalMilliseconds) > 0 ? TimeSpan.FromMilliseconds(resolution - sw.Elapsed.TotalMilliseconds):TimeSpan.FromMilliseconds(1));
                sw.Reset();
                sw.Start();
            }
        }
    }
}

如果我启动应用程序,只要我单击控件,textbox1 就会开始更新,而 textbox2 不会,但是如果我直接更改 textbox2,它会抛出它已更改的通知,因此数据绑定正常或我认为.

问题是:

  • 如果直接修改而不是在用户控件的线程中调用,为什么它会起作用?我认为它与 xaml 文件中 textbox2 的声明有关,因为它搜索祖先,而 usercontrol 是一个孩子,但我不知道如何处理这个问题。

  • propertychanged 事件适用于类的每个属性,但是如果执行多线程处理,它可能会采用不正确的值并将一个事件误认为另一个事件?

  • 我确定我没有正确使用 WPF 的规范,也许这会以某种方式影响代码,例如从用户控件调用 mainwindow 时:S

对于大量的代码和问题,我很抱歉,但我开始喜欢 WPF,当我没有让事情正常工作时,我感到有些沮丧。我搜索了类似的问题,但只发现人们在必须从主 UI 更新控件属性时遇到问题,而不是像我这样的其他方式

4

2 回答 2

1

首先不要绝望!WPF 对每个人都有一个陡峭的学习曲线。

添加属性更改后,您的方法是正确的。要通知代码中的更改,您必须提出它以让 UI 知道。但是我发现代码有点过于复杂。我想让您知道另一种解决方案,以防您发现它有用

您可以将数据放在一个类中,然后将其分配给 xaml 的 DataContext。完成此操作后,您就非常接近实现 MVVM 模式,在我看来,这是使用 WPF 的最佳方式。我放了一些示例代码

主窗口.xaml

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <TextBox Text="{Binding Value1}"/>
        <TextBox Text="{Binding Value2}"/>
    </StackPanel>
</Window>

主窗口.xaml.cs

using System.Windows;

namespace WpfApplication1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            MyClass myClass = new MyClass();
            myClass.Value1 = 1;
            myClass.Value2 = 2;
            DataContext = myClass;
        }
    }

    public class MyClass
    {
        public int Value1 { get; set; }
        public int Value2 { get; set; }
    }
}

对于子用户控件,您可以创建另一个类并将其分配给 UserControl DataContext。这使事情保持在一起和整洁。这也避免了执行“Application.Current.Windows.OfType().First().datavalue = count;”的需要 你可以直接修改你的绑定类

希望能帮助到你

于 2013-05-30T14:01:28.910 回答
0

做一些测试,我尝试向 datavalue2 属性添加与 datavalue 相同的条件,就是这样,在更新值后触发属性更改事件

        public int datavalue2
        {
            get
            {
                return mydatavalue2;
            }
            set{
                mydatavalue2 = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this,
                        new PropertyChangedEventArgs(
                        "datavalue2"));
                }
            }
        }

令人惊讶的是,尽管没有必要使用它,但它确实有效。我想知道如果我直接修改它是否会引发一些不兼容,因为它会引发 2 个事件,但它工作正常,只为 textbox2 引发了一个事件......我仍然对这种方法感到不安。

于 2013-05-30T13:29:50.970 回答