数据绑定基本上是巫术

时间:2017-04-05 13:56:58

标签: 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

我尝试将文本块单向绑定到字符串源,并在我的代码运行时自动更新...但它似乎永远不会更新。至于所有我正在使用的对象...我开始学习C#的原始愿望是创建我自己的程序,可以通过互联网将视频从任何类型的输入流传输到我的手机......显然我很长从那以后。非常感谢您的帮助!

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();
            }
        }
    }

dataHolder

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 个答案:

答案 0 :(得分:4)

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

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

  • 并试图以几种不同的方式进行绑定

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

从XAML开始

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

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

只选择一个c或本地

然后这个

<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接口将动作直接绑定到你的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完整而且相对简单)

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

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

然后你的Xaml会像这样绑定

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

这使我们很好地接受了绑定的主题, 绑定需要在Xaml或Code后面完成而不是两者都有,所以你有两个但后面的代码是不正确的,我建议单独使用XAML

完成所有这些操作会使您的代码落后于此

public MainWindow()
{
    InitializeComponent();
}

答案 1 :(得分:2)

您的问题是您正在创建两个dataholder个实例并在绑定到另一个时操纵一个实例。

第一个实例由您在Window构造函数aka域dh中创建,第二个实例由以下XAML创建:

<Window.Resources>
    <c:dataHolder x:Key="source"/>
</Window.Resources>

要快速解决问题,请将Window构造函数dh字段定义为公共属性DH

   public dataholder DH {get; set;}

...然后在您的Window XAML绑定Window.DataContext中,如下所示:

<Window 
  <!-- all the other attributes -->
  DataContext="{Binding DH, RelativeSource={RelativeSource Self}}">