WPF – I am trying to bind a progressbar value to a Method that calculates a percentage based on 2 textboxes – all inside a treeviewitem

huangapple go评论74阅读模式
英文:

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.

WPF – I am trying to bind a progressbar value to a Method that calculates a percentage based on 2 textboxes – all inside a treeviewitem

MainWindow.xaml

&lt;Window x:Class=&quot;Project_Management_App___Test_02.MainWindow&quot;
        xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
        xmlns:d=&quot;http://schemas.microsoft.com/expression/blend/2008&quot;
        xmlns:mc=&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;
        xmlns:local=&quot;clr-namespace:Project_Management_App___Test_02&quot;
        xmlns:i=&quot;http://schemas.microsoft.com/xaml/behaviors&quot;
        DataContext=&quot;{Binding RelativeSource={RelativeSource self}}&quot;
        mc:Ignorable=&quot;d&quot;
        Title=&quot;MainWindow&quot; Height=&quot;450&quot; Width=&quot;800&quot;&gt;

    &lt;StackPanel&gt;
        &lt;DockPanel&gt;
            &lt;Menu DockPanel.Dock=&quot;Top&quot;&gt;
                &lt;MenuItem Header=&quot;_File&quot;&gt;
                    &lt;MenuItem Header=&quot;New&quot; Click=&quot;MenuItem_Click&quot; /&gt;
                    &lt;MenuItem Header=&quot;Open&quot; /&gt;
                    &lt;MenuItem Header=&quot;Save&quot; /&gt;
                    &lt;Separator /&gt;
                    &lt;MenuItem Header=&quot;Exit&quot; /&gt;
                &lt;/MenuItem&gt;
            &lt;/Menu&gt;
        &lt;/DockPanel&gt;

        &lt;Grid&gt;

            &lt;TreeView Name=&quot;treeview&quot; Grid.Row=&quot;0&quot;&gt;
                
                &lt;TreeView.ItemTemplate&gt;
                    
                    &lt;HierarchicalDataTemplate DataType=&quot;{x:Type local:Task}&quot; ItemsSource=&quot;{Binding Items}&quot;&gt;
                        &lt;StackPanel Orientation=&quot;Horizontal&quot;&gt;
                            &lt;Label&gt;Task Name&lt;/Label&gt;
                            &lt;TextBox Width=&quot;120&quot; VerticalAlignment=&quot;Center&quot; Text=&quot;Text Goes here&quot;&gt;&lt;/TextBox&gt;
                            &lt;ProgressBar Width=&quot;200&quot; Height=&quot;10&quot; Value=&quot;{Binding Path=GetProgressPercentage}&quot; Margin=&quot;4&quot;&gt;&lt;/ProgressBar&gt;
                            &lt;Label&gt;Hours Total&lt;/Label&gt;
                            &lt;TextBox Width=&quot;30&quot; VerticalAlignment=&quot;Center&quot; Text=&quot;{Binding Path=HrsTotal, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}&quot;&gt;
                            &lt;/TextBox&gt;
                            &lt;Label&gt;Hours Remaining&lt;/Label&gt;
                            &lt;TextBox Width=&quot;30&quot; VerticalAlignment=&quot;Center&quot; Text=&quot;{Binding HrsRemaining, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}&quot;&gt;&lt;/TextBox&gt;
                            &lt;Button Margin=&quot;4&quot; Width=&quot;20&quot; Command=&quot;{Binding Path=AddBtnClick}&quot; &gt;+&lt;/Button&gt;
                            &lt;Button Margin=&quot;4&quot; Width=&quot;20&quot; Command=&quot;{Binding Path=DelBtnClick}&quot; &gt;-&lt;/Button&gt;
                        &lt;/StackPanel&gt;
                    &lt;/HierarchicalDataTemplate&gt;
                    
                &lt;/TreeView.ItemTemplate&gt;
                
                &lt;TreeView.ItemContainerStyle&gt;
                    &lt;Style TargetType=&quot;TreeViewItem&quot;&gt;
                        &lt;Setter Property=&quot;IsExpanded&quot; Value=&quot;{Binding IsExpanded}&quot;/&gt;
                    &lt;/Style&gt;
                &lt;/TreeView.ItemContainerStyle&gt;
                
            &lt;/TreeView&gt;

        &lt;/Grid&gt;
    &lt;/StackPanel&gt;
&lt;/Window&gt;

Task.cs

class Task : TreeViewItemBase
    {

        public Task()
        {
            this.Items = new ObservableCollection&lt;Task&gt;();
        }

        public ObservableCollection&lt;Task&gt; Items { get; set; }
        public ObservableCollection&lt;Task&gt; ParentItems { get; set; }

        private int _hrsTotal;
        private int _hrsRemaining;

        public int HrsTotal
        {
            get { return _hrsTotal; }
            set
            {
                if (_hrsTotal != value)
                {
                    _hrsTotal = value;
                    this.NotifyPropertyChanged(&quot;HrsTotal&quot;);
                }
            }
        }
        public int HrsRemaining
        {
            get { return _hrsRemaining; }
            set
            {
                if (_hrsRemaining != value)
                {
                    _hrsRemaining = value;
                    this.NotifyPropertyChanged(&quot;HrsRemaining&quot;);
                }
            }
        }

        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 =&gt; this.AddNewTask(), param =&gt; this.CanAddTask());
                }
                return _addBtnClick;
            }
        }

        public ICommand DelBtnClick
        {
            get
            {
                if(_delBtnClick == null)
                {
                    _delBtnClick = new RelayCommand(param =&gt; this.DeleteTask(), param =&gt; 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(&quot;IsExpanded&quot;);
                }
            }
        }

        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&lt;object&gt; _execute;
        readonly Predicate&lt;object&gt; _canExecute;

        public RelayCommand(Action&lt;object&gt; execute) : this(execute, null) { }
        public RelayCommand(Action&lt;object&gt; execute, Predicate&lt;object&gt; canExecute)
        {
            if(execute == null)
            {
                throw new ArgumentNullException(&quot;execute&quot;);   
            }
            _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; }
    }
}

并在 HrsRemainingHrsTotal 更改时调用
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>



huangapple
  • 本文由 发表于 2023年3月31日 20:17:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/75898445.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定