c# - 数据绑定(bind)基本上是巫术

标签 c# wpf xaml data-binding

我不是一个聪明人。我花了几个小时阅读许多不同的先前提出的问题并试图让这个东西工作,但我仍然缺少一些东西而且我不确定它是什么。当我意识到它是什么时,我可能会感到尴尬……但我从下面的第二个链接中得到的印象是,让事情以任何其他方式更新真的不应该这样做。

这些是我已经读过的内容:

WPF databinding not updating

How do I refresh visual control properties

Refreshing a WPF window on demand

Data binding overview on MSDN

我正在尝试将一个文本 block 单向绑定(bind)到一个字符串源,并让它在我的代码运行时自动更新......但它似乎永远不会更新。至于所有对象我正在使用...我开始学习 C# 的最初愿望是创建我自己的程序,该程序可以通过 Internet 将视频从任何类型的输入流流式传输到我的手机...显然我离那个还有很长的路要走。非常感谢您的帮助!

XAML

  <Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-
compatibility/2006"
        xmlns:c="clr-namespace:WpfApp1"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <c:dataHolder x:Key="source"/>
    </Window.Resources>
    <Window.DataContext>
        <Binding Source="{StaticResource source}"/>
    </Window.DataContext>

    <Grid>
        <TextBox x:Name="tb1" HorizontalAlignment="Left" Height="22" 
Margin="45,35,0,0" TextWrapping="Wrap" Text="Enter IP" 
VerticalAlignment="Top" Width="195"/>
        <Button x:Name="Connect" Content="Connect" 
HorizontalAlignment="Left" Margin="390,239,0,0" VerticalAlignment="Top" 
Width="75" Click="Connect_Click"/>
        <TextBlock x:Name="mblock" Text="{Binding Path=Message, Mode=OneWay, 
UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Height="47" 
Margin="45,105,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="195"/>
    </Grid>
</Window>

代码隐藏

public partial class MainWindow : Window
{
    private dataHolder dh;
    public MainWindow()
    {
        dh = new dataHolder();
        dh.Message = "Initialize";
        InitializeComponent();
    }

    Binding myBinding = new Binding("myDataProperty");

    private void Connect_Click(object sender, RoutedEventArgs e)
    {
        TcpListener server = null;
        try
        {
            myBinding.Source = dh;
            mblock.SetBinding(TextBlock.TextProperty, myBinding);
            //Set TcpListener on port 13000.
            Int32 port = 13000;
            IPAddress localAddr = IPAddress.Parse("192.168.32.137");
            server = new TcpListener(localAddr, port);
            server.Start();
            Byte[] bytes = new Byte[256];
            String data = null;
            //Enter the listening loop.
            while (true)
            {
                dh.Message = "Waiting for a connection";
                TcpClient client = server.AcceptTcpClient();
                data = null;
                NetworkStream stream = client.GetStream();
                int i;
                while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
                {
                        data = System.Text.Encoding.ASCII.GetString(bytes,0, 
i);
                        dh.Message = "Received:" + data;
                        data = data.ToUpper();
                        data = data + "Sucka";
                        byte[] msg = 
System.Text.Encoding.ASCII.GetBytes(data);
                        stream.Write(msg, 0, msg.Length);
                        dh.Message = "Sent:" + data;
                    }
                    client.Close();
                }
            }
            catch (SocketException ex)
            {
                string nastyE;
                nastyE = ex.Message;
                dh.Message = "Socket Exception" + nastyE;
            }
            finally
            {
                server.Stop();
            }
        }
    }

数据持有者

public partial class dataHolder : INotifyPropertyChanged
{
    private string message;
    public string Message
    {
        get
        {
            return message;
        }
        set
        {
            message = value;
            NotifyPropertyChanged("Message");
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

最佳答案

您遇到的主要问题是您是多余的,当您使用多个示例选择不同的方式来执行相同的任务并且没有经验来意识到它们是相同的时,很可能会发生这种情况

  • 您正在创建 2 个数据持有者,一个用于 XAML,一个用于代码隐藏,因为它们不相同,这意味着您的后端不会更新您的前端。

  • 并尝试以几种不同的方式进行绑定(bind)

我建议您阅读 this guide to MVVM因为这是一个非常容易理解的解释

从 XAML 开始

你不需要指定命名空间两次

xmlns:c="clr-namespace:WpfApp1"
xmlns:local="clr-namespace:WpfApp1"

随便选一个c或者local

然后这个

<Window.Resources>
    <c:dataHolder x:Key="source"/>
</Window.Resources>
<Window.DataContext>
    <Binding Source="{StaticResource source}"/>
</Window.DataContext>

和构造函数逻辑

private dataHolder dh;
public MainWindow()
{
    dh = new dataHolder();
    dh.Message = "Initialize";
    InitializeComponent();
}

可以缩短为

<Window.DataContext>
    <c:dataHolder Message="Initialize"/>
</Window.DataContext>

如果你需要从后面的代码中访问添加这个属性

public dataHolder dh => DataContext as dataHolder ;

尽管您通常不这样做,但使用 ICommand 接口(interface)直接将操作绑定(bind)到您的 VM,这看起来像这样

public class CallbackCommand : ICommand
{
    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
        => CanExecuteCallback?.Invoke(parameter) ?? true;

    public void Execute(object parameter)
        => Callback(parameter);

    private Action<object> _Callback;

    public Action<object> Callback
    {
        get { return _Callback; }
        set
        {
            _Callback= value;
            CanExecuteChanged?.Invoke(this, EventArgs.Empty);
        }
    }
    private Predicate<object> _CanExecuteCallback;
    public Predicate<object> CanExecuteCallback
    {
        get { return _CanExecuteCallback; }
        set
        {
            _CanExecuteCallback= value;
            CanExecuteChanged?.Invoke(this, EventArgs.Empty);
        }
    }
}

(这个已经有很多版本了,PRISMS的DelegateCommand比较完整,比较简单)

然后在你的数据 Holder 类中你有一个属性

public CallbackCommand Connect {get;} = new CallbackCommand ()
{
    Callback = <<your dataHolders connect method>>
}

然后您的 Xaml 将像这样绑定(bind)

<Button Content="Connect" Command="{binding Connect}"/>

这很好地把我们带到了绑定(bind)的主题上, 绑定(bind)需要在 Xaml 或代码背后完成,而不是两者兼而有之,但背后的代码不正确,我建议单独使用 XAML

做所有这些将减少你的代码背后

public MainWindow()
{
    InitializeComponent();
}

关于c# - 数据绑定(bind)基本上是巫术,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43233328/

相关文章:

wpf - 绑定(bind)到装饰元素的祖先

c# - 在类中映射相同对象类型的属性会调用 Equals 和 GetHashCode

c# - 如何在 WCF 中定义数据契约?

wpf - 如何使 Resharper 解析 CustomBinding MarkupExtension 的路径

wpf - 资源字典中的资源未得到应用

C# WPF - 如何组合数据触发器和触发器?

c# - 如何检查数组列表中是否存在数组

C# 设计选择——COM 对象还是另一个类?

c# - 防止 WPF DataGrid 中的多行选择

c# - Windows App Store - 拉伸(stretch)图标