初始化后WPF数据绑定不起作用.没有绑定错误



我的ViewViewModel之间的数据绑定有问题。绑定仅在初始化时运行,然后不会更新。代码按预期运行,每个输出窗口都没有绑定错误,但UI上的绑定不会更新。

程序设置:

  • WPF
  • C#
  • 棱镜V8.1.97
  • MVVM
  • .NET Framework 4.7.2

我尝试过的东西:

  • XAML设置为直接绑定到具有INotifyPropertyChanged的属性
  • XAML设置为查找UserControl类型的RelativeSource
  • RelayCommand来更新UI,使用和不使用Invoke来更新主线程
  • BackgroundWorker来更新UI,使用和不使用Invoke来更新主线程
  • DelegateCommand来更新UI,使用和不使用Invoke来更新主线程
  • i.Interaction.EventTriggersClick调用主线程上的UI更新

其中的每个人都将运行代码,但不会更新UI。我已经为BackgroundWorkerDelegate voidDispatcher.BeginInvoke留下了我在程序中尝试过的一些代码。我写过一些程序,从来没有遇到过这个问题。创建程序时是否设置错误?

更新:Visual Studio 2019似乎有问题。这可能只是我的版本,因为我写的其他程序不再工作。这通常可以按预期运行。

UserControl我正在尝试使用它进行一个简单的绑定。我在底部用Mode=TwoWay创建了一个Textbox,看看TextBlock是否会更新。

<UserControl x:Class="MRC.Module.ModStatus.Views.ModStatusManagerView"
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:MRC.Module.ModStatus.Views"

xmlns:prism="http://prismlibrary.com/"  

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:cconv="clr-namespace:MRC.Framework.Core.Data.Converters;assembly=MRC.Framework.Core"

mc:Ignorable="d" 
d:DesignHeight="450" d:DesignWidth="800" >
<UserControl.Resources>
<cconv:RemoveSpacesConverter x:Key="IntToStringConvert"/>
</UserControl.Resources>
<Grid>
<Grid.Background>
<SolidColorBrush Color="#222222"/>
</Grid.Background>

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>

<Border BorderBrush="DodgerBlue" BorderThickness="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Border BorderThickness="1" BorderBrush="DodgerBlue" VerticalAlignment="Stretch" Height="{Binding ActualHeight, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}}">
<StackPanel VerticalAlignment="Stretch">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Height="20" Margin="0,6" Content="{Binding StartStop}" Width="100"
>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click" >
<i:InvokeCommandAction Command="{Binding ProgressCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>

</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock  Margin="5,0"  HorizontalAlignment="Center" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.Health, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource IntToStringConvert}}" FontSize="16" Foreground="White"/>
<TextBlock  Margin="5,0"  Text="{Binding TestingText}" Foreground="White" FontSize="16" AutomationProperties.Name="TestText"/>
</StackPanel>
<!--
Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.Health, UpdateSourceTrigger=PropertyChanged}"
-->
<ProgressBar Maximum="100" Minimum="0"
VerticalAlignment="Bottom" Height="25" Margin="5" />
<TextBox Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.TestingText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="16" Height="30" Margin="5" Width="100"/>
</StackPanel>

</Border>
</Grid>

</Grid>
</Border>
<TextBlock HorizontalAlignment="Center" Grid.Column="1" VerticalAlignment="Center" Text="Mod Status" 
FontSize="30" Foreground="Black"/>
</Grid>
</Grid>
</UserControl>

背后的代码

public partial class ModStatusManagerView : UserControl
{
public ModStatusManagerView()
{
InitializeComponent();
DataContext = new ModStatusManagerViewModel();
}

}

ViewModel

public class ModStatusManagerViewModel : ViewModelBase
{
#region Variables
private readonly BackgroundWorker worker = new BackgroundWorker();
private delegate void UpdateUIDelgate(string health, string Status);
#endregion
#region Commands
public ICommand ProgressCommand { get; private set; }
private void Testing(string health, string Status)
{
try
{

}
catch(Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}

}
private bool CanProgressExecute()
{
return true;
}
private void Progress()
{
try
{

System.Windows.Application.Current.Dispatcher.Invoke(() => 
{
Health = 0;
StartStop = "Stop";
TestingText = "Initialized";
for (int i = 0; i < 99; i++)
{
Health = i;
System.Windows.Application.Current.MainWindow.UpdateLayout();
System.Threading.Thread.Sleep(100);
}
TestingText = "Completed";
System.Windows.MessageBox.Show("Complete");
}, System.Windows.Threading.DispatcherPriority.Render);
/*if (!System.Windows.Application.Current.Dispatcher.CheckAccess())
{
System.Windows.Application.Current.Dispatcher.BeginInvoke(
new UpdateUIDelgate(Testing), "Stop", "Initialized");
return;
}*/

// System.Windows.Application.Current.MainWindow.Dispatcher.BeginInvoke(
// new UpdateUIDelgate(Testing), "Stop", "Initialized");

}
catch(Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}       
}

public ICommand ProgressOffCommand { get; }
private void ProgressOff()
{
System.Windows.Application.Current.Dispatcher.Invoke(() =>
{
StartStop = "Start";
});
}
#endregion
#region Constructor
public ModStatusManagerViewModel()
{
this.ProgressCommand = new RelayCommand(Progress);
//this.ProgressOffCommand = new RelayCommand(ProgressOff);
}
#endregion
#region Properties
public bool IsEnabled
{
get { return _isEnabled; }
set { SetProperty(ref _isEnabled, value); }
}
private bool _isEnabled = true;
/// <summary>
/// Gets or Sets the Max Health
/// </summary>
public string StartStop
{
get { return _startStop; }
set { SetProperty(ref _startStop, value); }
}
private string _startStop = "Start";

/// <summary>
/// Gets or Sets the Max Health
/// </summary>
public bool IsOn
{
get { return _isOn; }
set { SetProperty(ref _isOn, value); }
}
private bool _isOn = false;
/// <summary>
/// Gets or Sets the Max Health
/// </summary>
public double MaxHealth
{
get { return _maxHealth; }
set { SetProperty(ref _maxHealth, value); }
}
private double _maxHealth = 100;
/// <summary>
/// Gets or Sets the Min Health
/// </summary>
public double MinHealth
{
get { return _minHealth; }
set { SetProperty(ref _minHealth, value); }
}
private double _minHealth = 0;
/// <summary>
/// Gets or Sets the Min Health
/// </summary>
public double Health
{
get { return _Health; }
set { SetProperty(ref _Health, value); }
}
private double _Health = 0;
public string TestingText
{
get { return _testingText; }
set { SetProperty(ref _testingText, value); }
}
private string _testingText = "Waiting";
#endregion
#region Events
#endregion
#region Methods
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
while (IsOn)
{
Random rnd = new Random();
Health = rnd.Next(0, 100);
System.Threading.Thread.Sleep(150);
}
System.Threading.Thread.Sleep(150);
}
}
private void worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
//update ui once worker complete his work
}
#endregion
}

如果有人希望看到INotifyPropertyChanged的实施

public abstract class ViewModelBase : ModelBase
{
public ViewModelBase()
{
}
~ViewModelBase()
{
}
public bool HasAnyErrors { get; set; }
}

这是ViewModelBase实现的ModelBase

public abstract class ModelBase : BindableBase, INotifyPropertyChanged
{
protected ModelBase()
{
}
~ModelBase() { }
public bool HasIssues { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public static event PropertyChangedEventHandler StaticPropertyChanged = delegate { };
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public static void OnStaticPropertyChanged(string propertyName)
{
StaticPropertyChanged?.Invoke(
typeof(ViewModelBase),
new PropertyChangedEventArgs(propertyName));
}
}

这是RelayCommand

public class RelayCommand : ICommand
{
#region Private Members
private Action _action;
private Action<object> _actionOb;
Action<object> _execteMethod;
Func<object, bool> _canexecuteMethod;
#endregion
#region Public Events
/// <summary>
/// Basic Command
/// </summary>
public event EventHandler CanExecuteChanged = (sender, e) => { };
#endregion
#region Constructors
/// <summary>
/// Default Constructor
/// </summary>
/// <param name="action"></param>
public RelayCommand(Action action)
{
_action = action;
}
/*public RelayCommand(System.Collections.ObjectModel.ObservableCollection<Frames.Model.Category> category, Action<object> action)
{
_actionOb = action;
}*/

/// <summary>
/// Default Constructor that passes an object
/// </summary>
/// <param name="execteMethod"></param>
/// <param name="canexecuteMethod"></param>
public RelayCommand(Action<object> execteMethod, Func<object, bool> canexecuteMethod)
{
_execteMethod = execteMethod;
_canexecuteMethod = canexecuteMethod;
}
/// <summary>
/// Default Constructor that determines if an action can execute before executing
/// </summary>
/// <param name="action"></param>
/// <param name="CanExecute"></param>
public RelayCommand(Action action, bool CanExecute)
{
if (CanExecute)
return;
_action = action;
}
public RelayCommand(Action<object> action, bool CanExecuteOb)
{
_actionOb = action;
}
#endregion
#region Command Methods
/// <summary>
/// Returns True if bool Parameter is not busy
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public bool CanExecute(object parameter)
{
if (parameter != null)
{
try
{
if ((bool)parameter)
return false;
return true;
}
catch
{
return true;
}
}
else
{
return true;
}

}
public bool CanExecuteOb(object parameter)
{
return true;
}
/// <summary>
/// Executes an Action that is not busy
/// </summary>
/// <param name="parameter"></param>
public void Execute(object parameter)
{
Task.Run(async () =>
{
_action();
});
}
/// <summary>
/// Executes an Action that is not busy with a <href="Param">
/// </summary>
/// <param name="param"></param>
public void ExecuteOb(object param)
{
Task.Run(async () =>
{
_actionOb(param);
});
}
#endregion
}

UPDATE:SetProperty的功能似乎已经停止工作。

我从更改了我的属性

public string TestingText
{
get { return _testingText; }
set { SetProperty(ref _testingText, value);}
}
private string _testingText = "Testing";

public string TestingText
{
get { return _testingText; }
set
{
if (_testingText == value)
return;
_testingText = value;
OnPropertyChanged("TestingText");
}
}
private string _testingText = "Testing";

现在一切正常。

最新更新