英文:
WPF - I am trying to bind a progressbar value to a Method that calculates a percentage based on 2 textboxes - all inside a treeviewitem
问题
我试图将进度条与一个返回整数的方法绑定,目前我的特定问题是这个。
我试图制作一个基本的Microsoft Project风格的WPF任务管理器作为学习练习。我对C#、WPF和MVVM模式非常陌生。我正在向树视图添加“任务”并使用“添加”按钮为该“任务”创建子任务。然后,我想要在文本框中输入“总工时”和“剩余工时”的数字,然后在进度条中看到任务完成的百分比。目前,当我输入数字时,没有发生任何事情,我真的不知道自己在做什么。
MainWindow.xaml
<Window x:Class="Project_Management_App___Test_02.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:Project_Management_App___Test_02"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
DataContext="{Binding RelativeSource={RelativeSource self}}"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel>
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="_File">
<MenuItem Header="New" Click="MenuItem_Click" />
<MenuItem Header="Open" />
<MenuItem Header="Save" />
<Separator />
<MenuItem Header="Exit" />
</MenuItem>
</Menu>
</DockPanel>
<Grid>
<TreeView Name="treeview" Grid.Row="0">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Task}" ItemsSource="{Binding Items}">
<StackPanel Orientation="Horizontal">
<Label>Task Name</Label>
<TextBox Width="120" VerticalAlignment="Center" Text="Text Goes here"></TextBox>
<ProgressBar Width="200" Height="10" Value="{Binding Path=GetProgressPercentage}" Margin="4"></ProgressBar>
<Label>Hours Total</Label>
<TextBox Width="30" VerticalAlignment="Center" Text="{Binding Path=HrsTotal, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</TextBox>
<Label>Hours Remaining</Label>
<TextBox Width="30" VerticalAlignment="Center" Text="{Binding HrsRemaining, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<Button Margin="4" Width="20" Command="{Binding Path=AddBtnClick}" >+</Button>
<Button Margin="4" Width="20" Command="{Binding Path=DelBtnClick}" >-</Button>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding IsExpanded}"/>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
</Grid>
</StackPanel>
</Window>
Task.cs
class Task : TreeViewItemBase
{
public Task()
{
this.Items = new ObservableCollection<Task>();
}
public ObservableCollection<Task> Items { get; set; }
public ObservableCollection<Task> ParentItems { get; set; }
private int _hrsTotal;
private int _hrsRemaining;
public int HrsTotal
{
get { return _hrsTotal; }
set
{
if (_hrsTotal != value)
{
_hrsTotal = value;
this.NotifyPropertyChanged("HrsTotal");
}
}
}
public int HrsRemaining
{
get { return _hrsRemaining; }
set
{
if (_hrsRemaining != value)
{
_hrsRemaining = value;
this.NotifyPropertyChanged("HrsRemaining");
}
}
}
public int GetProgressPercentage()
{
if(this.HrsTotal != 0)
{
return 100 -((this.HrsRemaining / this.HrsTotal) * 100);
}
else { return 0; }
}
private ICommand _addBtnClick;
private ICommand _delBtnClick;
public ICommand AddBtnClick
{
get
{
if(_addBtnClick == null)
{
_addBtnClick = new RelayCommand(param => this.AddNewTask(), param => this.CanAddTask());
}
return _addBtnClick;
}
}
public ICommand DelBtnClick
{
get
{
if(_delBtnClick == null)
{
_delBtnClick = new RelayCommand(param => this.DeleteTask(), param => this.CanDelTask());
}
return _delBtnClick;
}
}
private bool CanAddTask()
{
return true;
}
private bool CanDelTask()
{
if(ParentItems != null) { return true; }
else { return false; }
}
public void AddNewTask()
{
Task newTask = new Task();
newTask.ParentItems = this.Items;
this.Items.Add(newTask);
this.IsExpanded = true;
}
public void DeleteTask()
{
ParentItems.Remove(this);
}
}
TreeViewItemBase.cs
class TreeViewItemBase : INotifyPropertyChanged
{
private bool isExpanded;
public bool IsExpanded
{
get { return this.isExpanded; }
set
{
if(value != this.isExpanded)
{
this.isExpanded = value;
NotifyPropertyChanged("IsExpanded");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
RelayCommand.cs
class RelayCommand : ICommand
{
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
public RelayCommand(Action<object> execute) : this(execute, null) { }
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if(execute == null)
{
throw new ArgumentNullException("execute");
}
_execute = execute; _canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
我期望在“总工时”文本框中
英文:
My specific problem at the moment is I am trying to update a progress bar by binding it to a method which returns an int.
I'm trying to make a basic Microsoft Project style task manager in WPF as a learning exercise. I'm very new to C#, WPF and the MVVM pattern. I'm adding 'Tasks' to a tree view with an 'Add' button that creates children for that 'Task'. I then want to be able to put in numbers for 'Total Hours' and 'Hours Remaining' inside Text Boxes and then see the percentage of the task done in a progress bar. At the moment when I put in numbers nothing is happening and I really don't know what I'm doing.
MainWindow.xaml
<Window x:Class="Project_Management_App___Test_02.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:Project_Management_App___Test_02"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
DataContext="{Binding RelativeSource={RelativeSource self}}"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel>
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="_File">
<MenuItem Header="New" Click="MenuItem_Click" />
<MenuItem Header="Open" />
<MenuItem Header="Save" />
<Separator />
<MenuItem Header="Exit" />
</MenuItem>
</Menu>
</DockPanel>
<Grid>
<TreeView Name="treeview" Grid.Row="0">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Task}" ItemsSource="{Binding Items}">
<StackPanel Orientation="Horizontal">
<Label>Task Name</Label>
<TextBox Width="120" VerticalAlignment="Center" Text="Text Goes here"></TextBox>
<ProgressBar Width="200" Height="10" Value="{Binding Path=GetProgressPercentage}" Margin="4"></ProgressBar>
<Label>Hours Total</Label>
<TextBox Width="30" VerticalAlignment="Center" Text="{Binding Path=HrsTotal, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</TextBox>
<Label>Hours Remaining</Label>
<TextBox Width="30" VerticalAlignment="Center" Text="{Binding HrsRemaining, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<Button Margin="4" Width="20" Command="{Binding Path=AddBtnClick}" >+</Button>
<Button Margin="4" Width="20" Command="{Binding Path=DelBtnClick}" >-</Button>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding IsExpanded}"/>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
</Grid>
</StackPanel>
</Window>
Task.cs
class Task : TreeViewItemBase
{
public Task()
{
this.Items = new ObservableCollection<Task>();
}
public ObservableCollection<Task> Items { get; set; }
public ObservableCollection<Task> ParentItems { get; set; }
private int _hrsTotal;
private int _hrsRemaining;
public int HrsTotal
{
get { return _hrsTotal; }
set
{
if (_hrsTotal != value)
{
_hrsTotal = value;
this.NotifyPropertyChanged("HrsTotal");
}
}
}
public int HrsRemaining
{
get { return _hrsRemaining; }
set
{
if (_hrsRemaining != value)
{
_hrsRemaining = value;
this.NotifyPropertyChanged("HrsRemaining");
}
}
}
public int GetProgressPercentage()
{
if(this.HrsTotal != 0)
{
return 100 -((this.HrsRemaining / this.HrsTotal) * 100);
}
else { return 0; }
}
private ICommand _addBtnClick;
private ICommand _delBtnClick;
public ICommand AddBtnClick
{
get
{
if(_addBtnClick == null)
{
_addBtnClick = new RelayCommand(param => this.AddNewTask(), param => this.CanAddTask());
}
return _addBtnClick;
}
}
public ICommand DelBtnClick
{
get
{
if(_delBtnClick == null)
{
_delBtnClick = new RelayCommand(param => this.DeleteTask(), param => this.CanDelTask());
}
return _delBtnClick;
}
}
private bool CanAddTask()
{
return true;
}
private bool CanDelTask()
{
if(ParentItems != null) { return true; }
else { return false; }
}
public void AddNewTask()
{
Task newTask = new Task();
newTask.ParentItems = this.Items;
this.Items.Add(newTask);
this.IsExpanded = true;
}
public void DeleteTask()
{
ParentItems.Remove(this);
}
}
TreeViewItemBase.cs
class TreeViewItemBase : INotifyPropertyChanged
{
private bool isExpanded;
public bool IsExpanded
{
get { return this.isExpanded; }
set
{
if(value != this.isExpanded)
{
this.isExpanded = value;
NotifyPropertyChanged("IsExpanded");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
RelayCommand.cs
class RelayCommand : ICommand
{
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
public RelayCommand(Action<object> execute) : this(execute, null) { }
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if(execute == null)
{
throw new ArgumentNullException("execute");
}
_execute = execute; _canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
I am expecting to put in a number for the 'Total hours' Textbox then put in a number for the 'Hours remaining' Textbox and the see the percentage bar reflect the progress.
答案1
得分: 1
你不绑定到方法。你绑定到属性。当属性引发 INotifyPropertyChanged.PropertyChanged
事件时,绑定会自动更新。
你可以使用:
public int ProgressPercentage
{
get
{
if (this.HrsTotal != 0)
{
return 100 - ((this.HrsRemaining / this.HrsTotal) * 100);
}
else { return 0; }
}
}
并在 HrsRemaining
或 HrsTotal
更改时调用
OnPropertyChanged(nameof(ProgressPercentage))
。
英文:
You do not bind to methods. You bind to properties. When properties raise the INotifyPropertyChanged.PropertyChanged
event the binding gets updated automatically.
you could use:
public int ProgressPercentage
{
get
{
if(this.HrsTotal != 0)
{
return 100 -((this.HrsRemaining / this.HrsTotal) * 100);
}
else { return 0; }
}
}
and call
OnPropertyChanged(nameof(ProgressPercentage))
when HrsRemaining
or HrsTotal
have changed.
答案2
得分: 0
[![在此输入图片描述][1]][1]
[1]: https://i.stack.imgur.com/9X9Vk.png
感谢 @LaniusExcubitor,我成功获得了我想要的内容。我只需要将ProgressPercentage添加为一个属性,并添加一个用于计算百分比的方法,然后将ProgressPercentage变量设置为输出。
```csharp
public float HrsTotal
{
get { return _hrsTotal; }
set
{
_hrsTotal = value;
CalculatePercent();
this.NotifyPropertyChanged(nameof(ProgressPercentage));
}
}
public float HrsRemaining
{
get { return _hrsRemaining; }
set
{
_hrsRemaining = value;
CalculatePercent();
this.NotifyPropertyChanged(nameof(ProgressPercentage));
}
}
public double ProgressPercentage
{
get
{
return this._progressPercentage;
}
set
{
this._progressPercentage = value;
}
}
private void CalculatePercent()
{
ProgressPercentage = 100 - (HrsRemaining / HrsTotal * 100);
}
Note: The code is provided in the code block as requested, and the text outside of the code block remains in the original language.
<details>
<summary>英文:</summary>
[![enter image description here][1]][1]
[1]: https://i.stack.imgur.com/9X9Vk.png
Thanks @LaniusExcubitor I was able to get what I was after. I just needed to add the ProgressPercentage as a Property and add a method to calculate the percentage and then set the ProgressPercentage variable with the output.
public float HrsTotal
{
get { return _hrsTotal; }
set
{
_hrsTotal = value;
CalculatePercent();
this.NotifyPropertyChanged(nameof(ProgressPercentage));
}
}
public float HrsRemaining
{
get { return _hrsRemaining; }
set
{
_hrsRemaining = value;
CalculatePercent();
this.NotifyPropertyChanged(nameof(ProgressPercentage));
}
}
public double ProgressPercentage
{
get
{
return this._progressPercentage;
}
set
{
this._progressPercentage = value;
}
}
private void CalculatePercent()
{
ProgressPercentage = 100 - (HrsRemaining / HrsTotal * 100);
}
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论