6

キーボードで新しい値を入力せずに、マウスだけでセルの値 (式ではなく定数) を簡単に変更できるようにしたいと考えています。

このようなスクロールバーにより、ユーザーは他の数式やチャートで何が起こるかを動的に観察できます。

値を含むセルをクリックすると、スクロールバー (または他のデバイス) がセルの下 (またはセルの右) に表示されます。このデバイスを使用すると、マウスだけでセルの値を変更できます。スクロールバーの最小値と最大値を定義できる必要があります。定義されていない場合、最小値と最大値は、現在の値の 30% (最小) と 170% (最大) と見なされます。別のセルをクリックすると、「古い」スクロールバーが消え、クリックしたセルの下に新しいスクロールバーが表示されます。スクロールバーが表示されるセルを定義する可能性があるはずです(他のセルでは表示されません)。

1つのセルの値のみを変更する通常のExcelスクロールバー以外の何かが必要で、シート全体に何百ものスクロールバーを散らしたくありません。

私の調査から、選択されたセルに応答するワークシートまたはワークブックにイベントを設定できることがわかりました。そのセルがスクロールバーを表示できるセルかどうかを確認できます。その場合、コードで新しいスクロール バーを作成するか、既存のスクロール バーを表示して、スクロール バーをアクティブ セルの下に配置することができます。スクロールバーを変更すると、セルの値に影響する可能性があります。15 桁の 10 進数の値を避けるために、値がどのように変化するかをある程度制御する必要があります。セルの選択を解除すると、スクロールバーを破棄するか、次に使用するまで非表示にすることができます。

アップデート

質問に対する回答を送信しました。今、ツールの速度を改善することを楽しみにしています。

更新 2

ツールのパフォーマンスを改善するためのフォローアップの提案を次に示します。

4

5 に答える 5

6

このソリューションでは、WorkbookScrollBarが 1 つのクラスに結合されScrollValueます。Workbook_Openイベント ハンドラーで、このクラスのインスタンスが作成されます。

' ------------------------------------
' ThisWorkbook class module
' ------------------------------------
Option Explicit

Public ScrollValueWidget As ScrollValue

Private Sub Workbook_Open()
    Set ScrollValueWidget = New ScrollValue
    ScrollValueWidget.Max = 1000
    ScrollValueWidget.Min = 0
    ScrollValueWidget.Address = "C3:D10"
    ScrollValueWidget.DeleteScrollBars
End Sub

Private Sub Workbook_BeforeClose(Cancel As Boolean)
    Set ScrollValueWidget = Nothing
End Sub

ScrollValueクラスは をScrollBar処理SheetSelectionChangeし、ブック内のすべてのシートのイベントを 1 か所で処理します。セルが変更された後、スクロール バーが表示され、変更されたセルにリンクされます。スクロール バーが最小および最大制限になります。スクロールバーの値は対象セルの値に応じて自動設定されます。実際のセル値が最小値と最大値の範囲を超えると、警告が表示されます。

ScrollbarsクラスはOLEObjectsコレクションを使用します。各シートには、独自のスクロール バーがあります。したがって、各シートには一度に 1 つのスクロール バーしか存在しません。

注:ScrollBars Valueプロパティの値を負にすることはできません。クラスのインスタンス化プロパティを に設定しScrollValueますPublicNotCreatable

' ------------------------------------
' ScrollValue class module
' ------------------------------------

Option Explicit

Private minValue As Long
Private maxValue As Long
Private applyToAddress As String
Private WithEvents book As Workbook
Private scroll As OLEObject
Private scrolls As ScrollBars

Private Sub Class_Initialize()
    Set book = ThisWorkbook
    Set scrolls = New ScrollBars
End Sub

Private Sub Class_Terminate()
    Set scrolls = Nothing
    Set book = Nothing
End Sub

Private Sub book_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
    On Error GoTo ErrSheetSelectionChange

    Set scroll = scrolls.GetOrCreate(Sh) ' Get scroll for targer sheet
    Move Target ' Move scroll to new target cell

    Exit Sub

ErrSheetSelectionChange:
    MsgBox Err.Description, vbCritical
End Sub

Private Sub Move(targetRange As Range)
    ' Do not handle scroll for cells with formulas, not numeric or negative values
    If targetRange.HasFormula Then _
        Exit Sub

    If Not IsNumeric(targetRange.Value) Then _
        Exit Sub

    If targetRange.Value < 0 Then _
        Exit Sub

    If Application.Intersect(targetRange, ApplyToRange(targetRange.Worksheet)) Is Nothing Then _
        Exit Sub

    ' TODO: add code to handle when min/max not defined

    On Error GoTo ErrMove

    ' Move scroll to new target cell and show it
    With scroll
        .Top = targetRange.Top
        .Left = targetRange.Left + targetRange.Width + 2
        .Object.Min = Min
        .Object.Max = Max
        .LinkedCell = targetRange.Address
        .Visible = True
    End With

    Exit Sub

ErrMove:
    Dim errMsg As String
    errMsg = "Max = " & Max & " Min = " & Min & " Cell value = " & targetRange.Value & " must be between <Min, Max>." & Err.Description
    MsgBox errMsg, vbExclamation, "Scroll failed to show"
End Sub

Public Property Get Min() As Long
    Min = minValue
End Property

Public Property Let Min(ByVal newMin As Long)
    If newMin < 0 Then _
        Err.Raise vbObjectError + 1, "ScrollValue", "Min value musn't be less then zero"
    If newMin > maxValue Then _
        Err.Raise vbObjectError + 2, "ScrollValue", "Min value musn't be greater then max value"
    minValue = newMin
End Property

Public Property Get Max() As Long
    Max = maxValue
End Property

Public Property Let Max(ByVal newMax As Long)
    If newMax < 0 Then _
        Err.Raise vbObjectError + 3, "ScrollValue", "Max value musn't be less then zero"
    If newMax < minValue Then _
        Err.Raise vbObjectError + 4, "ScrollValue", "Max value musn't be less then min value"
    maxValue = newMax
End Property

Public Property Let Address(ByVal newAdress As String)
    If newAdress = "" Then _
        Err.Raise vbObjectError + 5, "ScrollValue", "Range address musn't be empty string"
    applyToAddress = newAdress
End Property

Public Property Get Address() As String
    Address = applyToAddress
End Property

Private Property Get ApplyToRange(ByVal targetSheet As Worksheet) As Range
    ' defines cell(s) for which scrollbar shows up
    Set ApplyToRange = targetSheet.Range(Address)
End Property

Public Sub DeleteScrollBars()
    scrolls.DelateAll
End Sub

' ------------------------------------
' ScrollBars class module
' ------------------------------------

Option Explicit

Private Const scrollNamePrefix As String = "ScrollWidget"

Private Sub Class_Terminate()
    DelateAll
End Sub

Private Function ScrollNameBySheet(ByVal targetSheet As Worksheet) As String
    ScrollNameBySheet = scrollNamePrefix & targetSheet.name
End Function

Public Function GetOrCreate(ByVal targetSheet As Worksheet) As OLEObject
    Dim scroll As OLEObject
    Dim scrollName As String

    scrollName = ScrollNameBySheet(targetSheet)

    On Error Resume Next
    Set scroll = targetSheet.OLEObjects(scrollName)
    On Error GoTo 0

    If scroll Is Nothing Then
        Set scroll = targetSheet.OLEObjects.Add(ClassType:="Forms.ScrollBar.1", _
            Left:=0, Top:=0, Width:=250, Height:=16)
        scroll.name = scrollName
        scroll.AutoLoad = True
        scroll.Object.Orientation = fmOrientationHorizontal
        scroll.Object.BackColor = &H808080
        scroll.Object.ForeColor = &HFFFFFF
    End If

    scroll.Enabled = True
    scroll.Locked = False
    scroll.LinkedCell = ""
    scroll.Visible = False

    Set GetOrCreate = scroll
End Function

Public Sub DelateAll()
    ' Deletes all scroll bars on all sheets if its name beginns with scrollNamePrefix

    Dim scrollItem As OLEObject
    Dim Sh As Worksheet

    For Each Sh In Worksheets
        For Each scrollItem In Sh.OLEObjects
            If scrollItem.name Like scrollNamePrefix & "*" Then
                scrollItem.Locked = False
                scrollItem.delete
            End If
        Next scrollItem
    Next Sh
End Sub

ここに画像の説明を入力

ScrollValue の動作を見る: YouTube ビデオ

于 2015-02-07T22:04:08.040 に答える
2

新しいセルの選択をキャッチするには、Workbook_SheetSelectionChange イベントを使用する必要があります。範囲ではなく 1 つのセルが選択されている場合にのみスクロールバーが表示されること、そのセルに数式が含まれていないこと、セルの値が数値であることを確認するために、いくつかのコントロールを構築する必要があります。baseValue = 0 の場合に値がどのように変化するかを考える必要があります (0 の 30% は 0 のままです)。

スクロール バーについては、フォーム コントロールまたは ActiveX コントロールを使用して、ワークシートに直接配置できます。前者は実装が簡単ですが、そのソリューションでは、スクロール中にセルの値が更新されません。これが必要な場合は、ActiveX コントロールを使用する必要があります。ただし、その場合は、CreateEventProc を使用して動的にイベント ハンドラーを生成する必要があります。コメントに記載されているように、このソリューションにはいくつかの重大な欠点があります。

したがって、3 番目の解決策は、ユーザーフォームを使用することです。この方法の利点の 1 つは、セルの値を元の値にリセットするボタンなど、他のコントロールを追加できることです。このソリューションについては、以下で説明します。

次のようなスクロールバーとボタンを含むユーザーフォームを作成し、MagicScrollBar という名前を付けます。

ここに画像の説明を入力

スクロールバーには、次のスクロール プロパティが必要です。

ここに画像の説明を入力

ユーザーフォームを右クリックし、コードの表示を選択して、次のコードをコピーします。

Option Explicit

Private Sub CommandButton1_Click()
    ActiveCell.Value = baseValue
    ScrollBar1.Value = 100
End Sub

Private Sub ScrollBar1_Change()
    UpdateCellValue
End Sub

Private Sub ScrollBar1_scroll()
    UpdateCellValue
End Sub

Private Sub UpdateCellValue()
    ActiveCell.Value = baseValue * ScrollBar1.Value / 100
End Sub

このコードを ThisWorkbook にコピーします。

Option Explicit

Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)

    Dim l As Double
    Dim t As Double
    Dim w As Double
    Dim h As Double

    MagicScrollBar.Hide

    If Selection.CountLarge = 1 Then
        If Not Intersect(Target, ActiveSheet.Cells) Is Nothing Then 'Replace ActiveSheet.Cells by range where scroll bar should appear
            If Target.HasFormula = False Then
                If IsNumeric(Target.Value) Then
                    If Target.Value <> 0 Then 'TO DO: Add some logic to handle cells with value = 0

                        baseValue = Target.Value

                         With MagicScrollBar
                            .ScrollBar1.Value = 100
                            .StartUpPosition = 0
                            .top = convertMouseToForm.top + Target.Height
                            .left = convertMouseToForm.left
                        End With

                        MagicScrollBar.Show vbModeless

                    End If
                End If
            End If
        End If
    End If

End Sub

最後に、このコードをモジュールにコピーします (最も複雑な部分は、ピクセル単位のマウス座標をポイント/インチ単位のユーザーフォーム座標に変換することであり、ここからコードを使用しましたhttp://ramblings.mcpher.com/Home/excelquirks/スニペット/マウス位置)

 Option Explicit

    Public baseValue As Double

    'Source: http://ramblings.mcpher.com/Home/excelquirks/snippets/mouseposition
    Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
    Declare Function GetDeviceCaps Lib "Gdi32" (ByVal hDC As Long, ByVal nIndex As Long) As Long
    Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hDC As Long) As Long

    Const LOGPIXELSX = 88
    Const LOGPIXELSY = 90

    Public Type tCursor
        left As Long
        top As Long
    End Type

    Private Declare Function GetCursorPos Lib "user32" (p As tCursor) As Long

Public Function pointsPerPixelX() As Double
    Dim hDC As Long
    hDC = GetDC(0)
    pointsPerPixelX = 72 / GetDeviceCaps(hDC, LOGPIXELSX)
    ReleaseDC 0, hDC
End Function

Public Function pointsPerPixelY() As Double
    Dim hDC As Long
    hDC = GetDC(0)
    pointsPerPixelY = 72 / GetDeviceCaps(hDC, LOGPIXELSY)
    ReleaseDC 0, hDC
End Function

Public Function WhereIsTheMouseAt() As tCursor
    Dim mPos As tCursor
    GetCursorPos mPos
    WhereIsTheMouseAt = mPos
End Function

Public Function convertMouseToForm() As tCursor
    Dim mPos As tCursor
    mPos = WhereIsTheMouseAt
    mPos.left = pointsPerPixelY * mPos.left
    mPos.top = pointsPerPixelX * mPos.top
    convertMouseToForm = mPos
End Function
于 2015-02-06T17:09:24.297 に答える
1

私はあなたの要件を完全には確信していませんが、あなたが試すのは正しいように思えます

Worksheet_SelectionChange(ByVal Target As Range)

繰り返しになりますが、スクロール バーが許可されているセルのロジック要件についてはよくわかりませんが、質問から判断すると、すでにそれを理解しています。したがって、選択したセルの下にスクロールバーを表示するには、次のようにします。

Set oYourScrollBar = ActiveSheet.Shapes("YourScrollBar")

If isSrollBarCell Then  'It is assumed you figured this part out!

  oYourScrollBar.Visible = True  'You may want to get rid of ScreenUpdating first for stylistic reasons.

  oYourScrollBar.Top = Target.Top + Target.Height  'Vert Distance to clicked cell + Height of clicked cell puts you under the cell
  oYourScrollBar.Left = Target.Left + (Target.Width - oYourScrollBar.Width) / 2  'Follow that one?

  oYourScrollBar.ControlFormat.LinkedCell = target.Address  'Change the linked cell of the scroll bar

Else

  oYourScrollBar.Visible = False  'Since there is no scrolling here, hide the scroll bar

End If

このコードは、MSDN のオンライン ドキュメントを参照して記述されていることに注意してください。私は現在 Linux マシンを使用しており、正確なデバッグを行うことはできません。また、ファイルと正確な構造にアクセスすることもできません。ヘルプ ファイルは最初はナビゲートするのが難しいですが、そこにはほとんどすべてのものがあります (「オブジェクト メンバー」の下を確認してください)。Shapes と Controls のオブジェクト階層は非常に細かいことに注意してください。多くのデバッグ テストを行い、ドキュメントのオブジェクト メンバーを読むことをお勧めします。

お知らせするために、場所コードの私のロジックは以下に基づいていました。

上 (ファイルの上端からの距離) - クリックされたセル (ターゲット) までの距離 + クリックされたセルの高さは、クリックされたセルの一番下になります。

左 (ファイルの左端からの距離) - クリックされたセル (ターゲット) までの距離に、クリックされたセルの幅の半分を加えた距離が、スクロール バーの端をターゲットの中心線に配置します。スクロール バーの幅の半分を引くと、スクロール バーの中心線がターゲットの中心線上に配置されます。これは、スクロール バーとセルのサイズが異なるためです。

私は以前にこのようなプロジェクトを行ったことがあるので、うまくいくはずですが、いつものように、自分で確認してください。コードの位置部分を正しく実行するために明示的にキャストする必要がある int から double への変換がいくつかある場合があります (vba では一般的ではありませんが、ランタイム エンジンが間違っていると推測した場合に発生します)。以前にこれらを使用したことがない場合は、ヘルプ ファイルで CInt()、CLng、CDbl() などを参照してください。

これがすべて役立つことを願っています。問題が発生した場合はお知らせください。

于 2012-12-18T00:15:06.083 に答える
-1

最も簡単な解決策は、セル内ドロップダウンのリストを使用してプログラムでデータ検証を割り当てることだと思います。したがって、ワークブックにはSourceDropDownシートがあります。

手順は次のとおりです。

  1. ドロップダウンが必要なすべてのセルが名前付き範囲であることを確認してください。行の挿入/削除を決定した場合、これは非常に貴重です。
  2. リストのすべての値を含むワークシートを作成します
  3. Worksheet Change イベントを使用して、コピーと貼り付けのイベントで検証が上書きされないようにします。

以下は、開始するためのサンプル コードです。

Private Sub Worksheet_Change(ByVal Target As Range)
    Dim cell As Range
    Set cell = ThisWorkbook.Worksheets(1).Range("MyNamedRange") ' change to whatever you have
    If Not Application.Intersect(cell, Range(Target.Address)) Is Nothing Then
        With cell.Validation
            .Delete
            .Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, Operator:= _
            xlBetween, Formula1:="=SourceDropDown!$T$2:$T$20"
            .ShowError = False
        End With
    End If
End Sub
于 2015-02-09T18:45:06.800 に答える