【问题标题】:Databinding is basically witchcraft数据绑定基本上是巫术
【发布时间】:2017-08-31 04:39:41
【问题描述】:

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

这些是我已经读过的东西:

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

数据持有者

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

【问题讨论】:

  • “我不是个聪明人”尝试新事物和四处试验是明智之举

标签: c# wpf xaml data-binding


【解决方案1】:

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

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

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

我建议你阅读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 behind 中完成,而不是两者都有,但后面的代码不正确,我建议单独使用 XAML

做这一切会将你的代码减少到这个程度

public MainWindow()
{
    InitializeComponent();
}

【讨论】:

    【解决方案2】:

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

    第一个实例由您在 Window 构造函数又名字段 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}}">
    

    【讨论】:

    • 在 xaml 中创建它通常比 CodeBhind 更可取,因为使用新的数据处理程序更改 Windows 数据上下文会再次破坏链接
    • 他并没有要求最好的方法来做到这一点。最好的方法是使用 MVVM 和命令。但是,他的所有代码都依赖于他的代码隐藏事件处理程序来操作字段,这就是我推荐这个解决方案的原因 - 更改较少。
    • OP显然是WPF的新手,可能来自winforms,所以不知道最佳实践,我们作为更有经验的开发人员的工作是引导新手走上正确的道路,你的回答是完全正确的这解决了他的问题,但可能导致他以不同的方式犯同样的错误,因此我的评论强调了这个问题
    猜你喜欢
    • 2018-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-24
    • 2012-04-29
    • 1970-01-01
    • 2010-10-15
    • 2012-11-08
    相关资源
    最近更新 更多