问题  如何以这种方式绑定?多绑定?

玻璃杯

知名会员
已加入
2019年11月22日
留言内容
126
编程经验
Beginner
这是一张有助于澄清我的问题的图片。

devenv_JSuauoULmT.png


三个文本框和一个文本块。我希望将TextBlock绑定到一个TextBox并镜像其内容,但是,如果我将鼠标悬停在另一个TextBox上,绑定将更改,因此TextBlock会镜像该TextBox的Text。将鼠标悬停在TextBox上只是选择一种方法的任意方法。我不需要事件或类似的帮助,只需绑定即可。我有一个要求,那就是将TextBlock绑定到VM属性,而永远不要直接绑定到TextBox的Text属性。

看起来很简单*手指交叉*
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,577
地点
弗吉尼亚州切萨皮克
编程经验
10+
感觉就像一个非常复杂的用户界面,但我的直觉是,如果您仅针对所有文本框为其对应的属性设置2种方式绑定,并且每个属性触发其他属性的属性更改通知,则所有操作都将在没有任何死锁或无限循环。关键将是集中更新视图模型(或模型)中发送更新和通知的位置。
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,968
地点
英国
编程经验
10+
@glasswizzard,我一直很忙,今天下午我开会开会,为YouTube会员做一些合同任务。

如果您仍然需要帮助,我明天晚上将为您写一个项目示例,因为我 可能 然后有一些空闲时间。

另外,“镜像”是什么意思?明确地说,您是要所有文本框在每个文本框的文本块中串联,还是只是将文本块的绑定属性更改为要悬停的新文本框?

万一我无法回复您:

如果需要绑定路线,则可以设置一个属性以保存您要悬停的文本框的值,然后分别更改文本。文本块将绑定到您的属性,即当它们获得鼠标焦点时,将设置文本框的值。通过实现INotifyPropertyChanged为 @跳伞 如果您在上一个示例中完成了此操作,则您将采用相同的设计,通过它您可以为所徘徊的对象更新绑定。我认为这实际上很简单。
 

玻璃杯

知名会员
已加入
2019年11月22日
留言内容
126
编程经验
Beginner
感觉就像一个非常复杂的用户界面,但我的直觉是,如果您仅针对所有文本框为其对应的属性设置2种方式绑定,并且每个属性触发其他属性的属性更改通知,则所有操作都将在没有任何死锁或无限循环。关键将是集中更新视图模型(或模型)中发送更新和通知的位置。

我试图做到这一点,但是我却一无所获:/

@glasswizzard,我一直很忙,今天下午我开会开会,为YouTube会员做一些合同任务。

如果您仍然需要帮助,我明天晚上将为您写一个项目示例,因为我 可能 然后有一些空闲时间。

我仍然无法做到这一点,谢谢您的任何帮助。慢慢来,一点也不着急。


另外,“镜像”是什么意思?明确地说,您是要所有文本框在每个文本框的文本块中串联,还是只是将文本块的绑定属性更改为要悬停的新文本框?

万一我无法回复您:

如果需要绑定路线,则可以设置一个属性以保存您要悬停的文本框的值,然后分别更改文本。文本块将绑定到您的属性,即当它们获得鼠标焦点时,将设置文本框的值。通过实现INotifyPropertyChanged为 @跳伞 如果您在上一个示例中完成了此操作,则您将采用相同的设计,通过它您可以为所徘徊的对象更新绑定。我认为这实际上很简单。

我害怕想什么 不会 对你直率。但是,是的,这是我想要的绑定路线。该文本块将仅镜像一个文本框。我会尝试您的建议,我觉得我已经足够理解,但我对上述跳伞运动员的建议也有相同的看法  :) 我会让你知道怎么回事。

编辑:好的,我现在真的很傻,我已经获得了99%的成功,我已经完成了您描述的设置,只是我不做的事情是更新文本框的MouseEnter事件中的textblock属性,这很荒谬,我为什么不想到  :(

无论如何,我将显示我的代码。我没有弄清楚一件事,那就是如何在鼠标悬停在特定文本框上的同时按每个键来更新文本块的文本。我确实尝试添加"UpdateSourceTrigger =属性更改"绑定到TextBlock,但没有任何区别。

MainWindow.xaml:
<Window x:Class="BindingApp.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:local="clr-namespace:BindingApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="108" Width="359">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <TextBox Margin="10" Text="{Binding Text1, Mode=TwoWay}" MouseEnter="TextBox_MouseEnter"/>
        <TextBox Grid.Column="1" Margin="10" Text="{Binding Text2, Mode=TwoWay}" MouseEnter="TextBox_MouseEnter"/>
        <TextBox Grid.Column="2" Margin="10" Text="{Binding Text3, Mode=TwoWay}" MouseEnter="TextBox_MouseEnter"/>

        <TextBlock Grid.Row="1" Grid.Column="1" Margin="10" Text="{Binding TextBlockText, FallbackValue=TextBlockText}" HorizontalAlignment="Center"/>

    </Grid>
</Window>

MainWindow.xaml.cs:
namespace BindingApp
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        ViewModel vm = new ViewModel();

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = vm;
        }

        private void TextBox_MouseEnter(object sender, MouseEventArgs e)
        {
            var textBox = (TextBox)sender;
            
            vm.TextBlockText = textBox.Text;           
        }
    }
}

ViewModel.cs:
namespace BindingApp
{
    public class ViewModel : INotifyPropertyChanged
    {
        private string _text1;       
        public string Text1
        {
            get
            {
                return _text1;
            }
        
            set
            {
                _text1 = value;
                OnPropertyChanged();
            }
        }

        private string _text2;       
        public string Text2
        {
            get
            {
                return _text2;
            }
        
            set
            {
                _text2 = value;
                OnPropertyChanged();
            }
        }

        private string _text3;       
        public string Text3
        {
            get
            {
                return _text3;
            }
        
            set
            {
                _text3 = value;
                OnPropertyChanged();
            }
        }

        private string _textBlockText = "TextBlockText";
        public string TextBlockText
        {
            get { return _textBlockText; }
            set
            {
                _textBlockText = value;
                OnPropertyChanged();
            }
        }


        #region  OnPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        internal void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
        #endregion
    }
}
 
Last edited:

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,577
地点
弗吉尼亚州切萨皮克
编程经验
10+
@GlassWizard:只是传递来自Sheepings的消息:您的代码看起来不错,但是绑定看起来不正确。他将专注于工作,将离开几个星期。

在接下来的几天里,如果我有时间,我将尝试赶上该线程(以及其他线程),以了解我可以做些什么。
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,968
地点
英国
编程经验
10+
@glasswizzard 我希望您会感激我花时间为您完成此操作,因为我可能通过不将未完成的代码转储到未完成的膝盖上而节省了很多时间,因为我也知道他还有更好的事情要做。而且,我本来应该为合同雇主完成工作,却花了我个人的工作时间。但是,如果您需要有关我的示例的进一步帮助,则您将需要等待一个mod来为您提供帮助,因为接下来的几周我将离开。我不太确定要持续多久。所以这是您要求的:

7YxCiMm.gif


这是我使用的代码。让我们从Main_ViewModel开始:
C#:
    public class Main_ViewModel
    {
        public ObservableValue New_Value { get; }
        public Main_ViewModel()
        {
            New_Value = new ObservableValue();
        }
    }
The main view model constructor returns a new class of ObservableValue of the value stored therein : private string TextBlock_Value; of this class. This is done through its GetSetter property :
C#:
        public string TextBlock_Text
        {
            get
            {
                if (string.IsNullOrEmpty(TextBlock_Value))
                    return "Nothing Detected";
                return TextBlock_Value;
            }
            set
            {
                TextBlock_Value = value;
                OnPropertyChanged();
            }
        }
在ObservableValue类中:
C#:
    public class ObservableValue : INotifyPropertyChanged
    {
        private string TextBlock_Value;
        public string TextBlock_Text
        {
            get
            {
                if (string.IsNullOrEmpty(TextBlock_Value))
                    return "Nothing Detected";
                return TextBlock_Value;
            }
            set
            {
                TextBlock_Value = value;
                OnPropertyChanged();
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string new_Value = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(new_Value));
        }
    }
Notice that we also have a public event PropertyChanged -- (Pretty much its a delegate to raise an event which helps with passing the method of the calling code when a component value of the INotifyPropertyChanged has been changed, thus raises the event therein) fires of the below method :
C#:
        protected void OnPropertyChanged([CallerMemberName] string new_Value = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(new_Value));
        }
快速移至Main Xaml.cs文件。我已经宣布 场地 : private Main_ViewModel main_ViewModel = new Main_ViewModel();
在构造函数中,我有:
C#:
        public Window1()
        {
            InitializeComponent();
            DataContext = main_ViewModel;
            Task Begin = new Task(() => Set_Default_Values());
            Begin.Start();
        }
Notice that I've added my DataContext to equal that of my main_ViewModel which is essentially our main_ViewModel => MainViewModel. Next, (I don't know why), but I update the default value of the textBlock Control by way of a new task. I just wasn't following the guidelines of the MVVM here, but it doesn't really matter as this is another way of updating your UI appropriately. Our Set_Default_Values() 方法 is what first sets the textBlock Text property :
C#:
        private void Set_Default_Values()
        {
            myTextBlock.Dispatcher.Invoke(new Delegate_Callback_To(UpdateUI_With_NewItem), new string[] { "Nothing Detected" });
        }
Because the Task is running on a non UI thread, you will need to call Dispatcher.Invoke() to first invoke the control by passing our : UpdateUI_With_NewItem 方法 to our Delegate. : public delegate void Delegate_Callback_To(string str_Value);. Note that this Delegate takes a string value; for 类型 安全起见,我使用的是字符串而不是基类对象,但是您可以将所需的任何对象用于传入的任何对象。如您所见,我传入的值是"Nothing Detected" to our delegate which will pass along this string from the Delegate Delegate_Callback_To to the UpdateUI_With_NewItem 方法 在以下代码中: myTextBlock.Dispatcher.Invoke(new Delegate_Callback_To(UpdateUI_With_NewItem), new string[] { "Nothing Detected" });

The UpdateUI_With_NewItem is the 方法 在此实例中用于更新我们的textBlock的值:
C#:
        public void UpdateUI_With_NewItem(string item_Value)
        {
            myTextBlock.Text = item_Value;
        }
您应该注意,在这一点上,这与绑定无关,我只是向您展示一种替代的安全方式来更新您的 用户界面 没有 MVVM 我希望您能遵循的模式。为了将其拉出并破坏某些东西,我将其留在了那里,因为它也值得您了解如何更新 用户界面 。无论您使用UI做什么,都不要更改UI值或允许 工人方法 在你的 用户界面 ,并且在使用此示例时,请始终遵循以下原则之一 用户界面 .

在这里重新了解MVVM和绑定。抱歉,绕道而行,但我希望您知道并喜欢将其提供给您,因为我写了它,而不是掏出一些东西为您提供一个破碎的示例,此外,它对您也很有教育意义。在继续之前,我还希望您查找内联代码和斜体突出显示的所有内容(自定义代码除外)。

接下来我添加了这些 鼠标移动 events :
C#:
        private void T0_MouseMove(object sender, MouseEventArgs e)
        {
            main_ViewModel.New_Value.TextBlock_Text = textBox0.Text;
        }

        private void T1_MouseMove(object sender, MouseEventArgs e)
        {
            main_ViewModel.New_Value.TextBlock_Text = textBox1.Text;
        }

        private void T2_MouseMove(object sender, MouseEventArgs e)
        {
            main_ViewModel.New_Value.TextBlock_Text = textBox2.Text;
        }
These events use the Main_ViewModel which depends on the ObservableValue to update the text property of the textBlock. Right so fare I've covered all the classes and explained how it works. You should also note that you will need the following using directives :
C#:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;

这是我的Xaml:
C#:
<Window xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
    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:local="clr-namespace:WPFTestApp" mc:Ignorable="d"
    x:Class="WPFTestApp.Window1" Title="Window1" x:Name="MyWindow1">
    <Window.DataContext>
        <local:Main_ViewModel/>
    </Window.DataContext>


    <ScrollViewer x:Name="ScrollView_Main" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Visible">
        <Grid x:Name="MyGrid" Width="Auto" ShowGridLines="false">
            <Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="365,455,0,0" VerticalAlignment="Top" Width="75"/>
            <TextBlock x:Name="myTextBlock" HorizontalAlignment="Left" Margin="215,409,0,0" TextWrapping="Wrap"
                       Text="{Binding New_Value.TextBlock_Text, Mode=TwoWay, UpdateSourceTrigger =属性更改}" VerticalAlignment="Top"/>
            <TextBox x:Name="textBox0" HorizontalAlignment="Left" Height="23" Margin="35,310,0,0" TextWrapping="Wrap" Text="Hello" VerticalAlignment="Top" Width="120" MouseMove="T0_MouseMove"/>
            <TextBox x:Name="textBox1" HorizontalAlignment="Left" Height="23" Margin="180,310,0,0" TextWrapping="Wrap" Text="My" VerticalAlignment="Top" Width="120" MouseMove="T1_MouseMove"/>
            <TextBox x:Name="textBox2" HorizontalAlignment="Left" Height="23" Margin="320,310,-21,0" TextWrapping="Wrap" Text="Wizzard" VerticalAlignment="Top" Width="120" MouseMove="T2_MouseMove"/>
        </Grid>
    </ScrollViewer>
</Window>

抱歉,我没有像Skydiver在以前的示例中使用自定义模板等所做的那样花哨的事情。但是,这里要注意的重要部分是我为绑定实际起作用而添加的内容。他们是:
  • 我们也在Codebehind文件中设置的DataContext。
  • 绑定:指向模型所组成的子属性/方法。 IE : Text="{Binding New_Value.TextBlock_Text, Mode=TwoWay, UpdateSourceTrigger =属性更改}"
请注意最后一个项目符号,绑定从属性开始 新值 : public ObservableValue New_Value { get; } of our Main Main_ViewModel。 I've also set the mode as TwoWay, and importantly added : UpdateSourceTrigger =属性更改 to this binding to work. Please remember, I will be away for some time as I am not a hobbyist programmer and one who is contracted out, so it may be a while before I come back. Any questions you have will have to be answered by one of the other contributors, that includes any corrections you may need help altering.

当您按照说明完成操作时,您应该具有类似于完整的.cs文件的内容,如下所示:
C#:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;

namespace WPFTestApp
{
    public partial class Window1 : Window
    {
        private Main_ViewModel main_ViewModel = new Main_ViewModel();
        public Window1()
        {
            InitializeComponent();
            DataContext = main_ViewModel;
            Task Begin = new Task(() => Set_Default_Values());
            Begin.Start();
        }

        public delegate void Delegate_Callback_To(string str_Value);

        public void UpdateUI_With_NewItem(string item_Value)
        {
            myTextBlock.Text = item_Value;
        }

        private void Set_Default_Values()
        {
            myTextBlock.Dispatcher.Invoke(new Delegate_Callback_To(UpdateUI_With_NewItem), new string[] { "Nothing Detected" });
        }
        private void T0_MouseMove(object sender, MouseEventArgs e)
        {
            main_ViewModel.New_Value.TextBlock_Text = textBox0.Text;
        }

        private void T1_MouseMove(object sender, MouseEventArgs e)
        {
            main_ViewModel.New_Value.TextBlock_Text = textBox1.Text;
        }

        private void T2_MouseMove(object sender, MouseEventArgs e)
        {
            main_ViewModel.New_Value.TextBlock_Text = textBox2.Text;
        }
    }
    public class ObservableValue : INotifyPropertyChanged
    {
        private string TextBlock_Value;
        public string TextBlock_Text
        {
            get
            {
                if (string.IsNullOrEmpty(TextBlock_Value))
                    return "Nothing Detected";
                return TextBlock_Value;
            }
            set
            {
                TextBlock_Value = value;
                OnPropertyChanged();
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string new_Value = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(new_Value));
        }
    }
    public class Main_ViewModel
    {
        public ObservableValue New_Value { get; }
        public Main_ViewModel()
        {
            New_Value = new ObservableValue();
        }
    }
}
顺便说一句,我建议像我一样在一个cs文件中打破嵌套类,并为您的模型,业务逻辑等创建层次结构文件夹结构-学习通过调试来阅读它,并注意报价在我的签名中

很快就和你们说话。
 

玻璃杯

知名会员
已加入
2019年11月22日
留言内容
126
编程经验
Beginner
哇,非常感谢您抽出宝贵的时间来做,这将给我一些耐心。

如果有人有答案,有一件小事让我挠头,为什么要使用scrollviewer?似乎没有必要,这是有原因的吗?
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,577
地点
弗吉尼亚州切萨皮克
编程经验
10+
滚动查看器在那里,是由于网格中各个部分的绝对定位和大小以及支持窗口的大小调整所致。如果让网格进行放置和调整大小,并为窗口设置固定的大小,则不需要滚动查看器。
 
最佳 底部