【问题标题】:How to SelectRow of a Task in Sub-Project (inside a Master-Project)如何在子项目中选择任务的行(在主项目内)
【发布时间】:2020-12-02 22:06:12
【问题描述】:

我有一个主项目,里面有几个子项目。

我想根据字段Text5 的值来格式化行的颜色。

当前问题:如何使用 VBA 代码引用第二个子项目中的行。 当我运行代码并修改任务 ID 5(在子项目 2 中)中的 Text5 值时,它会修改任务 ID 5 的颜色,但会修改子项目 1 的颜色。

如何使用SelectRow 并添加对所需子项目的引用?

我的代码(相关部分)

Sub FormatChangedTasks()

Dim SubPrj      As Subproject
Dim Tsk         As Task
Dim i           As Long

For Each SubPrj In ActiveProject.Subprojects
    ' compare the name of Sub-Project with the one saved in the "Stack" srray
    If SubPrj.SourceProject.Name = ModifiedPrjName Then
        For Each Tsk In SubPrj.SourceProject.Tasks
            If Not Tsk Is Nothing Then
                i = Tsk.ID

                ' check if Tsk.Text5 value has changed from value in "StatusStackArr" array
                If StatusStackArr(i - 1).StatusOldVal <> Tsk.Text5 Then
                    ' **** at the line below it selects the Row from the top
                    ' (not the desired Sub-Project) ******
                    SelectRow Row:=i, RowRelative:=False

                    ' --format entire row --
                    Select Case Tsk.Text5 ' Get the Field's used field, not name
                        Case "R", "Y", "G"
                            FontEx CellColor:=7, Color:=0
                            FontEx Italic:=False '  Font regular

                        Case "Complete"
                            FontEx Italic:=True '  Font Italic
                            FontEx CellColor:=15, Color:=14 ' Background Silver ; font Gray

                    End Select

               ' rest of code (un-relevant)

【问题讨论】:

    标签: vba ms-project


    【解决方案1】:

    挑战——在主项目中格式化一行

    要格式化表格中的文本,请使用FontEx 方法。此方法格式化活动(选定)单元格。要选择一行进行格式化,请使用SelectRow 方法。要使用此方法,您需要了解:

    1. 相对于活动选择的位置,或
    2. 它在可见任务中的绝对位置,或
    3. 根据唯一标识符查找任务

    对于选项 1 和 2,被过滤器或折叠摘要隐藏的任务意味着必须相应地调整 SelectRow 方法的行参数。很难知道哪些任务可能被隐藏。使用此方法的最佳方法是首先确保所有任务都是可见的(参见代码)。

    此外,在处理子项目时,计算绝对行位置是一项挑战,因为首先您需要将子项目的任务计数放在日程表中较高的位置,再加上主项目本身中的任何本机任务(尽管这是非典型的)。

    所有这些导致使用Find 方法,选项 3,作为在主项目中选择任务的最佳方式。要准确选择正确的任务,Find 方法必须与唯一字段一起使用。 Unique ID 字段是唯一保证唯一的字段。

    主项目中的唯一 ID

    将任务添加到项目时,会分配一个递增的唯一 ID,从 1 开始。当项目在主项目中合并时,唯一 ID 会通过添加种子值来更改,这样主项目中就不会出现重复项项目。

    种子值基于内部子项目“索引”。第一个子项目的任务的种子值为 4194304,第二个子项目的任务的种子值为 8388608 (4194304 * 2),以此类推。

    如果子项目从主项目中删除,它的“索引”不会被重新使用。同样,如果子项目在主项目中重新排列,“索引”值不会改变。因此,您不能使用子项目对象的Index 属性来获取用于创建种子的内部“索引”值,因为该属性仅指示子项目的顺序。 (注意,有一种方法可以获得这个内部值,但这超出了这个问题的范围。仅供参考,它还需要选择所有任务。)

    解决方案

    要在用户界面中选择任务,您需要知道它在主计划中的唯一 ID。如果您通过 SourceProject.Tasks 方法访问任务,您将访问其本机子项目中的任务,并且唯一 ID 将不包括种子值。例如,唯一 ID 将为 2,这在主项目中不是唯一的,因此不足以使用 Find 方法选择任务。

    由于您已经循环遍历计划中的所有任务,因此确定主唯一 ID 的最简单方法是循环遍历主项目中的本地任务。为此,请选择所有任务并循环选择。

    Sub FormatChangedTasks()
    
    ' show all tasks
    FilterClear
    SelectAll
    SummaryTasksShow (0)
    OutlineShowAllTasks
    
    Dim AllTasks As Tasks
    Set AllTasks = ActiveSelection.Tasks
    
    Dim Tsk As Task
    
    For Each Tsk In AllTasks
        If Not Tsk Is Nothing Then
            ' compare the name of Sub-Project with the one saved in the "Stack" srray
            If Tsk.Project = ModifiedPrjName Then
    
                ' check if Tsk.Text5 value has changed from prior value
                ' NOTE: use a dictionary here instead of an array
                If StatusStackArr(i - 1).StatusOldVal <> Tsk.Text5 Then
    
                    Find "Unique ID", "equals", Tsk.UniqueID
                    SelectRow
    
                    ' --format entire row --
                    Select Case Tsk.Text5 ' Get the Field's used field, not name
                        Case "R", "Y", "G"
                            FontEx CellColor:=7, Color:=0
                            FontEx Italic:=False '  Font regular
    
                        Case "Complete"
                            FontEx Italic:=True '  Font Italic
                            FontEx CellColor:=15, Color:=14 ' Background Silver ; font Gray
    
                    End Select
    
                ' rest of code (un-relevant)
            End If
        End If
    Next Tsk
    End Sub
    

    注意事项:

    • 选择所有任务是一项要求,除非您可以保证计划已经完全展开并且您选择选项 1 或 2。选择所有任务将花费 10-15 个子项目的一些时间,但其余代码将以接近相同的速度运行。
    • 如果任务按任务 ID 分组或不按任务 ID 排序,则选项 1 或 2 将取消所有投注。
    • 将先前的值存储在由任务 ID 索引的数组中不适用于主项目。相反,将以前的值存储在一个集合或字典中,以主唯一 ID 作为键。 See this SO post about using dictionaries in VBA.

    【讨论】:

    • 感谢您的详细回答。有没有办法使用查找“唯一 ID”方法,然后在不循环选择的情况下转到该行,我不喜欢选择所有任务,它会减慢代码运行时间,并且在屏幕上显示不好。主项目有时会有 10-15 个子项目,因此会大大降低代码运行时间。我只想循环通过修改后的任务的子项目。有没有这样的方法?
    • @ShaiRado 请参阅上面的编辑,了解为什么选择所有任务几乎是不可避免的以及为什么它真的不错。它可能看起来像一个 hack,就像一个糟糕的 Excel 宏,但事实并非如此,尤其是因为替代方案要么非常受限,要么难以实施,或者两者兼而有之。
    • 感谢您提供了惊人的详细答案。我已经开始计算每个子项目的行数,以及一些不错的数学。但是,我明天(以色列时间)会尝试您的解决方案,看看响应时间是否可以接受。如果你还醒着,“master unique ID”是不是和Task.UniqueID一样?还是有别的属性?
    • 顺便说一句,有没有办法在不使用 SelectRow 的情况下格式化任务的行背景颜色? (就像在 Excel VBA 中一样)
    • @ShaiRado 没有单独的唯一 ID 字段;在主项目中查看时,任务的唯一 ID 不同。例如,在查看 master 中的任务时,唯一 ID 可能是 8388611,但如果您直接打开该子项目,则任务唯一 ID 将为 3(8388611 mod 4194304)。
    猜你喜欢
    • 2012-12-03
    • 1970-01-01
    • 2020-02-05
    • 2012-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-07
    相关资源
    最近更新 更多