2009-06-19 31 views
5

Tôi muốn có/xác định id duy nhất cho mỗi hàng dữ liệu trong bảng dữ liệu Excel của mình - để tôi có thể sử dụng nó khi truyền dữ liệu trở đi và nó vẫn là giống nhau khi các hàng được thêm vào/xóa trên nó.Cách nhận/đặt id duy nhất cho ô trong Excel qua VBA

những suy nghĩ của tôi là sử dụng thuộc tính ID của Phạm vi (msdn link)

Vì vậy, tôi có một người dùng được xác định chức năng (UDF) mà tôi đặt trong mỗi hàng mà được/bộ ID như sau:

Dim gNextUniqueId As Integer 

Public Function rbGetId(ticker As String) 
    On Error GoTo rbGetId_Error 
    Dim currCell As Range 
    'tried using Application.Caller direct, but gives same error 
    Set currCell = Range(Application.Caller.Address) 
    If currCell.id = "" Then 
     gNextUniqueId = gNextUniqueId + 1 
     'this line fails no matter what value I set it to. 
     currCell.id = Str(gNextUniqueId) 
    End If 
    rbGetId = ticker & currCell.id 
    Exit Function 

    rbGetId_Error: 
    rbGetId = "!ERROR:" & Err.Description 
End Function 

Nhưng điều này không thành công ở dòng nhắc đến với

"ứng dụng xác định hoặc đối tượng được xác định lỗi"

Tôi nghĩ có lẽ đó là một trong những hạn chế của UDF, nhưng tôi cũng gặp lỗi tương tự nếu tôi thử mã đó từ mã được kích hoạt từ nút ruy-băng ...

Bất kỳ đề xuất nào khác về cách giữ id nhất quán - có lẽ Tôi nên cư trú các tế bào thông qua nút ribbon của tôi, tìm các tế bào không có ID và tạo/thiết lập giá trị di động của những người ...

EDIT: Như Ant nghĩ rằng, tôi có tấm bảo vệ, nhưng ngay cả trong một tế bào mở khóa nó vẫn không thành công. Unprotecting tờ sửa chữa vấn đề .... nhưng tôi đã sử dụng "Bảo vệ UserInterFaceOnly: = True" mà nên cho phép tôi làm điều này. Nếu tôi tự cho phép "Chỉnh sửa đối tượng" khi tôi bảo vệ trang tính thì nó cũng hoạt động, nhưng tôi không thấy tùy chọn có lập trình cho điều đó - và tôi cần gọi hàm Protect trong AutoOpen để bật tính năng UserInterfaceOnly ...

Tôi đoán tôi cần phải tắt/bật bảo vệ xung quanh cài đặt ID của tôi - giả sử rằng có thể được thực hiện trong UDF ... mà có vẻ như nó không thể, vì điều đó không hoạt động - không ActiveSheet.unprotect hoặc ActiveWorkbook.unprotect :(

Cảm ơn trước Chris

+1

Mã của bạn hoạt động tốt ở đây trong cả Excel 2003 và 2007. Sách của bạn không bị khóa hoặc giống như vậy? – Ant

Trả lời

0

Tôi thấy rằng nếu tôi bảo vệ trang tính bằng "Protect DrawingObjects: = False", UDF có thể đặt Id. Lạ thật.

Cảm ơn sự giúp đỡ của bạn về điều này.

1

đồng tình với Ant -. mã của bạn hoạt động tốt ở đây trên Excel 2003 SP3

. 0

Tôi cũng có thể sử dụng:

Set currCell = Application.Caller 
If Application.Caller.ID = "" Then 
    gNextUniqueId = gNextUniqueId + 1 
    'this line fails no matter what value I set it to. 
    currCell.ID = Str(gNextUniqueId) 
End If 

Aha! Tôi nghĩ tôi có nó.

Tôi nghĩ bạn đang gọi điều này từ một công thức mảng và chỉ được gọi là ONCE với phạm vi đầy đủ. Bạn không thể lấy ID cho một phạm vi - chỉ một ô duy nhất. Điều này giải thích vì sao Application.Caller.ID không thành công cho bạn, vì Range ("A1: B9"). ID tạo ra một Application-defined or object-defined error.

Khi bạn sử dụng Range(Application.Caller.Address) để nhận "ô", bạn chỉ trì hoãn lỗi này xuống dòng currCell.ID.

+0

Công thức không phải là phạm vi của nó, chỉ là một ô duy nhất. –

+0

Theo EDIT của bạn ở trên - bạn đã thử sử dụng ActiveSheet.Unprotect hoặc ActiveWorkbook.Unprotect chưa? Bạn luôn có thể tham khảo Cell.Parent nếu bạn muốn tránh bị rõ ràng với trang tính (một bước đi khôn ngoan). Tôi nghĩ bởi vì UDF là một phần của giao diện người dùng, là một công thức, nó đã bị ngăn cản thực hiện bất kỳ thay đổi nào đối với trang tính, nhưng nó chỉ là một linh cảm. –

1

Tôi nghĩ rằng chúng tôi có thể có một vài vấn đề xảy ra ở đây, nhưng tôi nghĩ rằng họ đang thử nghiệm các vấn đề, không phải vấn đề với chính mã đó. Đầu tiên, nếu bạn gọi hàm từ bất kỳ thứ gì ngoài một ô, như cửa sổ ngay lập tức, mã khác, vv Application.Caller sẽ không được đặt. Đây là những gì đang tạo ra đối tượng của bạn không tìm thấy lỗi. Thứ hai, nếu bạn sao chép/dán ô có chức năng, bạn cũng sẽ sao chép/dán ID. Vì vậy, bất cứ nơi nào bạn dán nó vào, đầu ra sẽ giữ nguyên. Nhưng nếu bạn chỉ cần sao chép văn bản (thay vì ô), và sau đó dán thì điều này sẽ hoạt động tốt. (Bao gồm việc sử dụng ứng dụng ban đầu của bạn của ứng dụng.)

+0

Cảm ơn người đứng đầu về các tính năng sao chép/dán của ID –

4

Okay ...

Nó xuất hiện rằng nếu bảng bị khóa, macro không có quyền ghi vào thông tin ở mức độ thấp như ID.

Tuy nhiên, tôi không nghĩ rằng có thể không bảo vệ trang tính trong UDF. Theo thiết kế, UDF bị hạn chế nghiêm trọng; Tôi nghĩ rằng có một công thức tế bào kiểm soát việc bảo vệ tấm sẽ phá vỡ các mô hình công thức rằng một công thức tế bào ảnh hưởng đến một tế bào duy nhất. Xem this page trên trang web của Microsoft để biết thêm chi tiết.

Tôi nghĩ điều này sẽ giới hạn các tùy chọn của bạn. Bạn phải:

  • bỏ bảo vệ tấm
  • bỏ UDF, sử dụng một sự kiện Worksheet_Change để nắm bắt những thay đổi tế bào và ghi vào ID có
  • sử dụng một UDF mà viết ID vào giá trị di động, thay vì lưu vào ID

Phương pháp UDF có nhiều vấn đề khi bạn đang cố gắng sử dụng thiết bị để tính toán ô đánh dấu vĩnh viễn trên trang tính. Tuy nhiên, đây là một ví dụ về UDF bạn có thể sử dụng để đóng một giá trị "vĩnh viễn" vào một ô, hoạt động trên các ô được mở khóa của một trang tính được bảo vệ. Điều này chỉ hoạt động cho các tế bào đơn (mặc dù nó có thể được điều chỉnh cho một công thức mảng).

Public Function CellMark() 

    Dim currCell As Range 
    Set currCell = Range(Application.Caller.Address) 

    Dim myId As String 
    ' must be text; using .value will cause the formula to be called again 
    ' and create a circular reference 
    myId = currCell.Text 

    If (Trim(myId) = "" Or Trim(myId) = "0") Then 
     myId = "ID-" & Format(CStr(gNextUniqueId), "00000") 
     gNextUniqueId = gNextUniqueId + 1 
    End If 

    CellMark = myId 

End Function 

Điều này hoàn toàn sai lầm. Tuy nhiên, việc sử dụng bản sao hoặc hộp điền sẽ giữ lại giá trị được sao chép trước đó. Chỉ bằng cách thiết lập rõ ràng các ô là một công thức mới, nó sẽ hoạt động. Nhưng nếu bạn nhập vào công thức vào ô một lần nữa (chỉ cần bấm vào nó, nhấn ENTER) một giá trị mới được tính toán - đó là hành vi ô chuẩn.

Tôi nghĩ rằng sự kiện Worksheet_Change là con đường để đi, trong đó có nhiều vĩ độ hơn. Dưới đây là một ví dụ đơn giản cập nhật ID của bất kỳ thay đổi ô nào. Nó có thể được điều chỉnh theo kịch bản cụ thể của bạn. Chức năng này cần phải được thêm vào mọi Bảng tính mà hành vi cài đặt ID được yêu cầu.

Private Sub Worksheet_Change(ByVal Target As Range) 

    Dim currCell As Range 
    Set currCell = Target.Cells(1, 1) 

    Dim currId As String 
    currId = currCell.ID 

    If Trim(currCell.ID) = "" Then 
     Target.Parent.Unprotect 
     currCell.ID = CStr(gNextUniqueId) 
     Target.Parent.Protect 
     gNextUniqueId = gNextUniqueId + 1 
    End If 

End Sub 

Lưu ý cuối cùng; trong mọi trường hợp, bộ đếm ID của bạn sẽ được đặt lại nếu bạn mở lại trang tính (ít nhất theo các chi tiết giới hạn được trình bày trong ví dụ của bạn).

Hy vọng điều này sẽ hữu ích.

+2

Cảm ơn bạn đã phân tích chi tiết, nhưng tôi thấy rằng nếu tôi bảo vệ trang tính bằng "Protect DrawingObjects: = False", UDF có thể đặt Id. Strange ... –

+0

Darn, cũng được thực hiện trên việc tìm kiếm điều đó. Bạn có hoàn toàn được sắp xếp ngay bây giờ hoặc vẫn còn những vấn đề gây hại không? –

1

Sự cố xảy ra với Application.Caller.

Vì bạn đang gọi nó từ một hàm do người dùng xác định, nó sẽ chuyển cho bạn một mô tả lỗi. Đây là nhận xét trong tệp Trợ giúp.

Bình luận

Thuộc tính này trả về thông tin về cách Visual Basic đã được gọi là, như thể hiện trong bảng sau.

Caller - Giá trị Return

  • Một chức năng tùy chỉnh vào trong một tế bào duy nhất - Một đối tượng Phạm vi xác định rằng tế bào
  • Một chức năng tùy chỉnh mà là một phần của công thức mảng trong một loạt các tế bào - Một Tầm đối tượng quy định cụ thể phạm vi của các tế bào
  • Một Auto_Open, Auto_Close, Auto_Activate, hoặc Auto_Deactivate vĩ mô - tên của tài liệu dưới dạng văn bản
  • Một vĩ mô được thiết lập bởi một trong hai OnDoubleClick hoặc OnEntry tài sản - tên của nhận dạng đối tượng biểu đồ hoặc di động refere nce (nếu có) mà macro áp dụng
  • Hộp thoại Macro (trình đơn Công cụ) hoặc bất kỳ người gọi nào không được mô tả ở trên - #REF! giá trị lỗi

Vì bạn đang gọi nó từ một hàm do người dùng xác định, những gì đang xảy ra là Application.Caller đang trả về một chuỗi mã lỗi cho biến phạm vi của bạn curCell. Nó KHÔNG gây ra lỗi mà trình xử lý lỗi của bạn sẽ nhận. Điều gì xảy ra sau đó là bạn tham khảo curCell, nó không thực sự là một phạm vi nữa. Trên máy của tôi, nó cố gắng thiết lập curCell = Range ("Error 2023"). Dù đối tượng đó là gì, nó có thể không có thuộc tính ID nữa và khi bạn cố gắng thiết lập nó, nó sẽ ném cho bạn lỗi đối tượng đó.

Đây là những gì tôi sẽ cố gắng ...

  1. Hãy thử loại bỏ xử lý lỗi của bạn và xem nếu VBA ném lên bất kỳ trường hợp ngoại lệ trên Range (Application.Caller.Address). Điều này sẽ không khắc phục được nó, nhưng nó có thể chỉ cho bạn đi đúng hướng.

  2. Hoặc thông qua logic hoặc Application.ActiveCell hoặc tuy nhiên bạn muốn làm điều đó, hãy tham chiếu ô trực tiếp. Ví dụ Phạm vi ("A1") hoặc Ô (1.1). Application.Caller.Address dường như không phải là một lựa chọn tốt để sử dụng.

  3. Thử sử dụng Option Explicit. Điều này có thể làm cho dòng nơi bạn đặt curCell ném lên một lỗi kể từ khi Range (Application.Caller.Address) không giống như nó đi qua một loạt trở lại, đó là datatype của curCell.

Các vấn đề liên quan