簡體   English   中英

在WPF中將UserControl的依賴項屬性綁定到MainWindow ViewModel

[英]Binding Dependency Property of UserControl to MainWindow ViewModel in WPF

請允許我將問題簡化為基本問題。

我的項目中有一個UserControl1。 它具有如下所示的TextBox:

<TextBox Text="{Binding TextProperty}"/>

在后面的代碼中,我有一個像這樣的依賴項屬性:

public string TextProperty
{
    get { return (string)GetValue(TextPropertyProperty); }
    set { SetValue(TextPropertyProperty, value); }
}

public static readonly DependencyProperty TextPropertyProperty = DependencyProperty.Register("TextProperty", typeof(string), typeof(UserControl1), new PropertyMetadata(null));

而且UserControl的構造函數很簡單

public UserControl1()
{
    InitializeComponent();  
    DataContext = this;  
}

在MainWindow中,我有這個:

<userctrl:UserControl1 TextProperty="{Binding ABC, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Grid.Row="1" Text="{Binding PQR, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>

現在,在MainWindow的viewModel中,我有:

    private string _abc;
    public string ABC
    {
        get { return _abc; }
        set 
        { _abc = "20";
            OnPropertyChanged();
        }
    }

    private string _pqr;

    public string PQR
    {
        get { return _pqr; }
        set
        {
            if(value != _pqr)
            {
                _pqr = value;
                ABC = PQR;
                OnPropertyChanged();
            }
        }
    }

現在,即使UserControl也應該復制我在文本框中編寫的內容,對嗎?

但事實並非如此。 我已經在ABC的設置器上設置了斷點。 它得到更新,但該更新未到達UserControl。 我的直覺說綁定無提示地失敗了,這是因為我已經在UserControl的構造函數中將DataContext設置this

我應該如何解決?

實際情況:

這是UserControl的XAML:

<UserControl x:Class="MyDiskTools.UserControls.NodeGrid.NodeGrid"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:MyDiskTools.UserControls.NodeGrid"             
             mc:Ignorable="d">
    <Grid>
        <Grid.Resources>
            <Style TargetType="Button">
                <Setter Property="Padding" Value="5"/>
                <Setter Property="BorderThickness" Value="1"/>
                <Setter Property="Command" Value="{Binding InputCommand}"/>
                <Setter Property="CommandParameter" Value="{Binding Path=Content, RelativeSource={RelativeSource Self}}"/>                
                <Style.Triggers>                    
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="BorderThickness" Value="5"/>
                        <Setter Property="FontSize" Value="20"/>
                        <Setter Property="FontFamily" Value="Times New Roman"/>
                    </Trigger>                    
                </Style.Triggers>
            </Style>
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <UniformGrid Grid.Row="0" Rows="1">
            <Button Content="A" />
            <Button Content="B" />
            <Button Content="C" />
            <Button Content="D" />
            <Button Content="E" />
            <Button Content="F" />
        </UniformGrid>
        <UniformGrid Grid.Row="1" Rows="1">
            <Button Content="G" />
            <Button Content="H" />
            <Button Content="I" />
            <Button Content="J" />
            <Button Content="K" />
            <Button Content="L" />
            <Button Content="M" />
        </UniformGrid>
        <UniformGrid Grid.Row="2" Rows="1">
            <Button Content="N" />
            <Button Content="O" />
            <Button Content="P" />
            <Button Content="Q" />
            <Button Content="R" />
            <Button Content="S" />
            <Button Content="T" />
        </UniformGrid>
        <UniformGrid Grid.Row="3" Rows="1">
            <Button Content="U" />
            <Button Content="V" />
            <Button Content="W" />
            <Button Content="X" />
            <Button Content="Y" />
            <Button Content="Z" />
        </UniformGrid>
        <TextBox Name="InputMessage" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" IsEnabled="False" Background="Beige" Grid.Row="4" Text="{Binding PasswordDisplay, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    </Grid>
</UserControl>

下面是代碼:

public partial class NodeGrid : UserControl
{
    public NodeGrid()
    {
        InitializeComponent();            
        InputCommand = new InputCharacterCommand(this);
        DataContext = this;
    }

    public string PasswordDisplay
    {
        get { return (string)GetValue(PasswordDisplayProperty); }
        set { SetValue(PasswordDisplayProperty, value); }
    }


    public static readonly DependencyProperty PasswordDisplayProperty =
        DependencyProperty.Register("PasswordDisplay", typeof(string), typeof(NodeGrid), new PropertyMetadata(""));


    private ICommand _inputCommand;

    public ICommand InputCommand
    {
        get
        {
            return _inputCommand;
        }

        set
        {
            _inputCommand = value;
        }
    }


    public void AddCharacter(string input)
    {
        if (input != null)
        {
            PasswordDisplay = string.Concat(PasswordDisplay, input);
        }
    }

    public bool InputAllowed()
    {
        if (PasswordDisplay == null)
        {
            return true;
        }

        if (PasswordDisplay.Length < 50)
        {
            return true;
        }

        return false;
    }

    private void OnPropertyChange([CallerMemberName] string property = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
    }

    public event PropertyChangedEventHandler PropertyChanged;

}

class InputCharacterCommand : ICommand
{
    private NodeGrid _vmodel;

    public InputCharacterCommand(NodeGrid vm)
    {
        _vmodel = vm;
    }

    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
    {
        return (_vmodel.InputAllowed());
    }

    public void Execute(object parameter)
    {
        _vmodel.AddCharacter(parameter as string);
    }
}

這是我在MainWindow中使用它的方式:

<StackPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
    <WrapPanel HorizontalAlignment="Center">
        <Label Content="Enter PIN"/>
        <TextBox CommandManager.PreviewCanExecute="HandleCanExecute" Foreground="Transparent" MinWidth="100" Padding="10,0" Text="{Binding PIN, UpdateSourceTrigger=PropertyChanged}"/>
    </WrapPanel>
    <customlock:NodeGrid  MinHeight="250" MinWidth="500" PasswordDisplay="{Binding NodeGridDisplay}"/>
    <Button VerticalAlignment="Center" HorizontalAlignment="Center" Content="Unlock!"/>
</StackPanel>

背后的代碼:

private void HandleCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    if (e.Command == ApplicationCommands.Cut ||
        e.Command == ApplicationCommands.Copy ||
        e.Command == ApplicationCommands.Paste)
        {
            e.CanExecute = false;
            e.Handled = true;
        }

}

現在,ViewModel:

    private string _PIN;

    public string PIN
    {
        get
        {
            return _PIN;
        }
        set
        {
            if(value != _PIN)
            {
                _PIN = value;                    
                OnPropertyChanged();
                NodeGridDisplay = HashIt(_PIN);
            }
        }
    }

    private string _nodeGridDisplay;

    public string NodeGridDisplay
    {
        get
        {
            return _nodeGridDisplay;
        }

        set
        {
            if (value != _nodeGridDisplay)
            {
                _nodeGridDisplay = value;
                OnPropertyChanged();
            }                
        }
    }

    private string HashIt(string input)
    {
        using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
        {
            return System.Text.Encoding.Default.GetString(md5.ComputeHash(System.Text.Encoding.ASCII.GetBytes(input))).GetHashCode().ToString();
        }
    }

預期功能是什么?

讓我說明一下。

可以輸入PIN,其哈希將顯示在NodeGrid中。 然后,用戶將單擊將與哈希連接的字母。 然后,用戶可以單擊解鎖。

您應該在UserControl使用ElementName或使用RaltiveResource來找到它,並且不應該使用DataContext = this。 (請參閱此答案此鏈接

給您的userControl命名並輸入:

<UserControl x:Class= ......
      Name="userControl">
     <TextBox Text="{Binding Text, ElementName = userControl}"/>
</UserControl>

請注意,您應該使用Text而不是TextProperty並確保將_abc = "20"更改為_abc = value

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM