研究心得------->Seskin控件包中SeskinEdit汉字问题的解决办法

公司采用Seskin控件包来开发。却发现SeskinEdit在使用汉字是有问题。主要是由汉字时光标定位不准。鼠标选字也选不准。
于是看了其代码。发现它在计算文本长度时采用的函数TextLength有问题。
其实TCanvas提供了一个TextLength方法,在去文本长度时汉字没有问题。
所以把这里替换下来就行了。
替换后的se_controls单元中的TSeCustomEdit的代码如下
 TSeCustomEdit = class(TSeCustomControl)
 private
   FText: WideString;
   FLMouseSelecting: boolean;
   FCaretPosition: integer;
   FSelStart: integer;
   FSelLength: integer;
   FFirstVisibleChar: integer;
   FPopupMenu: TSeCustomPopupMenu;
   FAutoSelect: boolean;
   FCharCase: TEditCharCase;
   FHideSelection: Boolean;
   FMaxLength: Integer;
   FReadOnly: Boolean;
   FOnChange: TNotifyEvent;
   FPasswordChar: WideChar;
   FPasswordKind: TPasswordKind;
   FTextAlignment: TAlignment;
   FActionStack: TEditActionStack;
   FPopupMenuDropShadow: boolean;
   FPopupMenuShowAnimationTime: integer;
   FPopupMenuBlendValue: integer;
   FPopupMenuShadowWidth: integer;
   FPopupMenuShowAnimation: TSeAnimationRec;
   FPopupMenuBlend: boolean;
   FContextMenuOptions: TSePopupMenuOptions;
   procedure UpdateFirstVisibleChar;
   procedure UpdateCaretePosition;
   procedure UpdateCarete;

   procedure WMGetDlgCode(var Msg: TWMGetDlgCode); message WM_GETDLGCODE;
   procedure WMCopy(var Message: TMessage); message WM_COPY;
   procedure WMPaste(var Message: TMessage); message WM_PASTE;
   procedure WMCut(var Message: TMessage); message WM_CUT;
   procedure WMUnDo(var Message: TMessage); message WM_UNDO;
   procedure WMContexMenu(var Message: TMessage); message WM_CONTEXTMENU;
   procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message
     WM_LBUTTONDBLCLK;
   { unicode }
   procedure WMImeStartComposition(var Message: TMessage); message
     WM_IME_STARTCOMPOSITION;
   procedure WMImeComposition(var Msg: TMessage); message WM_IME_COMPOSITION;
   { VCL messages }
   procedure CMEnabledChanged(var Msg: TMessage); message CM_ENABLEDCHANGED;
   procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
   procedure CMTextChanged(var Msg: TMessage); message CM_TEXTCHANGED;

   function GetSelText: WideString;
   function GetVisibleSelText: WideString;
   function GetNextWordBeging(StartPosition: integer): integer;
   function GetPrivWordBeging(StartPosition: integer): integer;
   function GetSelStart: integer;
   function GetSelLength: integer;
   function GetText: WideString;
   procedure SetText(const Value: WideString);
   procedure SetFont(Value: TFont);
   procedure SetCaretPosition(const Value: integer);
   procedure SetSelLength(const Value: integer);
   procedure SetSelStart(const Value: integer);
   procedure SetAutoSelect(const Value: boolean);
   procedure SetCharCase(const Value: TEditCharCase);
   procedure SetHideSelection(const Value: Boolean);
   procedure SetMaxLength(const Value: Integer);
   procedure SetPasswordChar(const Value: WideChar);
   procedure SetCursor(const Value: TCursor);
   procedure SetTextAlignment(const Value: TAlignment);
   procedure SetPasswordKind(const Value: TPasswordKind);
   procedure SetPopupMenuBlendValue(const Value: integer);
   procedure SetPopupMenuDropShadow(const Value: boolean);
   procedure SetPopupMenuShadowWidth(const Value: integer);
   procedure SetPopupMenuShowAnimation(const Value: TSeAnimationRec);
   procedure SetPopupMenuShowAnimationTime(const Value: integer);
   procedure SetPopupMenuBlend(const Value: boolean);
   procedure SetContextMenuOptions(const Value: TSePopupMenuOptions);
 protected
   function GetEditRect: TRect; virtual;
   function GetPasswordCharWidth: integer; virtual;
   function GetCharX(A: integer): integer;
   function GetCoordinatePosition(x: integer): integer;
   function GetSelRect: TRect; virtual;
   function GetAlignmentFlags: integer;

   procedure PaintBuffer; override;

   procedure PaintText; virtual;
   procedure PaintBackground(Rect: TRect; Canvas: TCanvas); virtual;
   procedure PaintSelectedText; virtual;
   procedure DrawPasswordChar(SymbolRect: TRect; Selected: boolean); virtual;

   function ValidText(NewText: WideString): boolean; virtual;
   function CanAutoSize(var NewWidth, NewHeight: Integer): Boolean; override;

   procedure BorderChanged; override;
   procedure HasFocus; override;
   procedure KillFocus; override;
   procedure MouseDown(Button: TMouseButton; Shift: TShiftState; x, y:
     integer);
     override;
   procedure MouseUp(Button: TMouseButton; Shift: TShiftState; x, y: integer);
     override;
   procedure MouseMove(Shift: TShiftState; x, y: integer); override;
   procedure KeyDown(var Key: Word; Shift: TShiftState); override;
   procedure KeyPress(var Key: Char); override;
   procedure SelectWord;
   procedure Change; dynamic;

   function CreatePopupMenu(AOwner: TComponent): TSeCustomPopupMenu; virtual;
   function CreatePopupMenuItem(AOwner: TComponent): TSeCustomItem; virtual;
   procedure BuildPopupMenu;
   procedure UpdatePopupMenuItems; virtual;
   procedure DoUndo(Sender: TObject);
   procedure DoCut(Sender: TObject);
   procedure DoCopy(Sender: TObject);
   procedure DoPaste(Sender: TObject);
   procedure DoDelete(Sender: TObject);
   procedure DoSelectAll(Sender: TObject);

   property CaretPosition: integer read FCaretPosition write SetCaretPosition;
   property PopupMenu: TSeCustomPopupMenu read FPopupMenu;
 public
   constructor Create(AOwner: TComponent); override;
   destructor Destroy; override;
   procedure Loaded; override;

   procedure ShowCaret; virtual;
   procedure HideCaret; virtual;

   procedure CopyToClipboard;
   procedure PasteFromClipboard;
   procedure CutToClipboard;
   procedure ClearSelection;
   procedure SelectAll;
   procedure Clear;

   procedure UnDo;

   procedure InsertChar(Ch: WideChar);
   procedure InsertText(AText: WideString);
   procedure InsertAfter(Position: integer; S: WideString; Selected: boolean);
   procedure DeleteFrom(Position, Length: integer; MoveCaret: boolean);

   property SelStart: integer read GetSelStart write SetSelStart;
   property SelLength: integer read GetSelLength write SetSelLength;
   property SelText: WideString read GetSelText;
 published
   property Anchors;
   property AutoSelect: boolean read FAutoSelect write SetAutoSelect default
     true;
   property AutoSize;
   property Blending;
   property BevelSides;
   property BevelInner;
   property BevelOuter;
   property BevelKind;
   property BevelWidth;
   property BorderWidth;
   property CharCase: TEditCharCase read FCharCase write SetCharCase default
     ecNormal;
   property Constraints;
   property Color;
   property Cursor write SetCursor;
   property DragCursor;
   property DragKind;
   property DragMode;
   property Enabled;
   property ImeMode;
   property ImeName;
   property Font write SetFont;
   property HideSelection: Boolean read FHideSelection write SetHideSelection
     default True;
   property MaxLength: Integer read FMaxLength write SetMaxLength default 0;
   property Performance;
   property ParentFont;
   property ParentShowHint;
   property PasswordKind: TPasswordKind read FPasswordKind write
     SetPasswordKind;
   property PasswordWideChar: WideChar read FPasswordChar write SetPasswordChar
     default WideChar(#0);
   property ContextMenuOptions: TSePopupMenuOptions read FContextMenuOptions
     write SetContextMenuOptions;
   property ReadOnly: Boolean read FReadOnly write FReadOnly default False;
   property ShowHint;
   property TabOrder;
   property TabStop default true;
   property Text: WideString read GetText write SetText;
   property TextAlignment: TAlignment read FTextAlignment write SetTextAlignment
     default taLeftJustify;

   property Visible;

   property OnChange: TNotifyEvent read FOnChange write FOnChange;
   property OnClick;
   property OnDblClick;
   property OnDragDrop;
   property OnDragOver;
   property OnEndDock;
   property OnEndDrag;
   property OnEnter;
   property OnExit;
   property OnKeyDown;
   property OnKeyPress;
   property OnKeyUp;
   property OnMouseDown;
   property OnMouseMove;
   property OnMouseUp;
   property OnStartDock;
   property OnStartDrag;
 end;



{ TSeCustomEdit ===============================================================}

constructor TSeCustomEdit.Create(AOwner: TComponent);
begin
 inherited;
 FActionStack := TEditActionStack.Create(Self);
 FContextMenuOptions := TSePopupMenuOptions.Create;

 Performance := kspDoubleBuffer;

 BevelKind := kbkSingle;
 BevelWidth := 1;
 BorderWidth := 3;

 TabStop := true;
 Width := 121;
 Height := 21;
 Color := clWindow;

 FTextAlignment := taLeftJustify;
 FAutoSelect := true;
 AutoSize := true;
 FCharCase := ecNormal;
 FHideSelection := true;
 FMaxLength := 0;
 FReadOnly := false;
 FPasswordChar := WideChar(#0);

 FLMouseSelecting := false;

 FCaretPosition := 0;
 FSelStart := 0;
 FSelLength := 0;
 FFirstVisibleChar := 1;

 ControlStyle := ControlStyle + [csCaptureMouse];

 FPopupMenuBlend := false;
 FPopupMenuBlendValue := 150;
 FPopupMenuDropShadow := false;
 FPopupMenuShadowWidth := 4;
 FPopupMenuShowAnimationTime := 300;

 Cursor := Cursor;
end;

destructor TSeCustomEdit.Destroy;
begin
 if FPopupMenu <> nil then
   FPopupMenu.Free;
 FContextMenuOptions.Free;
 FActionStack.Free;
 inherited;
end;

procedure TSeCustomEdit.Loaded;
begin
 inherited;
 AdjustSize;
end;

procedure TSeCustomEdit.HasFocus;
begin
 inherited;
 UpdateCarete;
 CaretPosition := 0;
 if AutoSelect then
   SelectAll;
end;

procedure TSeCustomEdit.KillFocus;
begin
 inherited;
 DestroyCaret;
 Invalidate;
end;

function TSeCustomEdit.GetCharX(a: integer): integer;
var
 WholeTextWidth    : integer;
 EditRectWidth     : integer;
begin
 Result := GetEditRect.Left;

 if PasswordKind <> pkNone then
   WholeTextWidth := Length(Text) * GetPasswordCharWidth
 else
   {WholeTextWidth := TextWidth(Canvas, Copy(Text, 1, Length(Text)),
     DT_NOPREFIX); }
   WholeTextWidth := Canvas.TextWidth(Copy(Text, 1, Length(Text)));

 if a > 0 then
 begin
   Canvas.Font.Assign(ControlFont);
   if PasswordKind <> pkNone then
   begin
     if a <= Length(Text) then
       Result := Result + (a - FFirstVisibleChar + 1) * GetPasswordCharWidth
     else
       Result := Result + (Length(Text) - FFirstVisibleChar + 1) *
         GetPasswordCharWidth;
   end
   else
   begin
     if a <= Length(Text) then
       Result := Result + Canvas.TextWidth(Copy(Text, FFirstVisibleChar, a -
         FFirstVisibleChar + 1))
         //Result := Result + TextWidth(Canvas, Copy(Text, FFirstVisibleChar, a - FFirstVisibleChar + 1), DT_NOPREFIX)
     else
       Result := Result + Canvas.TextWidth(Copy(Text, FFirstVisibleChar,
         Length(Text) - FFirstVisibleChar + 1));
     //Result := Result + TextWidth(Canvas, Copy(Text, FFirstVisibleChar, Length(Text) - FFirstVisibleChar + 1), DT_NOPREFIX);
   end;
 end;

 EditRectWidth := GetEditRect.Right - GetEditRect.Left;
 if WholeTextWidth < EditRectWidth then
   case TextAlignment of
     taRightJustify: Result := Result + (EditRectWidth - WholeTextWidth);
     taCenter: Result := Result + ((EditRectWidth - WholeTextWidth) div 2);
   end;
end;

function TSeCustomEdit.GetCoordinatePosition(x: integer): integer;
var
 CurX              : double;
 TmpX,
   WholeTextWidth,
   EditRectWidth   : integer;
begin
 Result := FFirstVisibleChar - 1;
 if Length(Text) = 0 then
   Exit;

 if PasswordKind <> pkNone then
   WholeTextWidth := Length(Text) * GetPasswordCharWidth
 else
   WholeTextWidth := Canvas.TextWidth(Copy(Text, 1, Length(Text)));
 //WholeTextWidth :=TextWidth(Canvas, Copy(Text, 1, Length(Text)), DT_NOPREFIX);

 EditRectWidth := GetEditRect.Right - GetEditRect.Left;
 TmpX := x;
 if WholeTextWidth < EditRectWidth then
   case TextAlignment of
     taRightJustify: TmpX := x - (EditRectWidth - WholeTextWidth);
     taCenter: TmpX := x - ((EditRectWidth - WholeTextWidth) div 2);
   end;

 if PasswordKind <> pkNone then
 begin
   Result := Result + (TmpX - GetEditRect.Left) div GetPasswordCharWidth;
   if Result < 0 then
     Result := 0
   else
     if Result > Length(Text) then
       Result := Length(Text);
 end
 else
 begin
   Canvas.Font.Assign(ControlFont);
   {CurX := GetEditRect.Left + TextWidth(Canvas, Text[FFirstVisibleChar],
     DT_NOPREFIX) / 2; }
   CurX := GetEditRect.Left + Canvas.TextWidth(Text[FFirstVisibleChar]) / 2;
   while (CurX < TmpX) and (Result + 1 <= Length(Text)) and (CurX <
     GetEditRect.Right) do
   begin
     //CurX := CurX + TextWidth(Canvas, Text[Result + 1], DT_NOPREFIX) / 2;
     CurX := CurX + Canvas.TextWidth(Text[Result + 1]) / 2;
     if Result + 1 + 1 <= Length(Text) then
       //CurX := CurX + TextWidth(Canvas, Text[Result + 1 + 1], DT_NOPREFIX) / 2;
       CurX := CurX + Canvas.TextWidth(Text[Result + 1 + 1]) / 2;
     Result := Result + 1;
   end;
 end;
end;

function TSeCustomEdit.GetEditRect: TRect;
begin
 with Result do
 begin
   Result := GetBorderRect;

   Canvas.Font.Assign(ControlFont);
   Result.Bottom := Result.Top + Canvas.TextHeight('Pq');
 end;
end;

function TSeCustomEdit.GetAlignmentFlags: integer;
begin
 case FTextAlignment of
   taCenter: Result := DT_CENTER;
   taRightJustify: Result := DT_RIGHT;
 else
   Result := DT_LEFT;
 end;
end;

procedure TSeCustomEdit.KeyDown(var Key: word; Shift: TShiftState);
var
 TmpS              : WideString;
 OldCaretPosition  : integer;
begin
 inherited KeyDown(Key, Shift);
 OldCaretPosition := CaretPosition;
 case Key of
   VK_END: CaretPosition := Length(Text);
   VK_HOME: CaretPosition := 0;
   VK_LEFT:
     if ssCtrl in Shift then
       CaretPosition := GetPrivWordBeging(CaretPosition)
     else
       CaretPosition := CaretPosition - 1;
   VK_RIGHT:
     if ssCtrl in Shift then
       CaretPosition := GetNextWordBeging(CaretPosition)
     else
       CaretPosition := CaretPosition + 1;
   VK_DELETE, 8:                       {Delete or BackSpace key was pressed}
     if not ReadOnly then
     begin
       if SelLength <> 0 then
       begin
         if Shift = [ssShift] then
           CutToClipboard
         else
           ClearSelection;
       end
       else
       begin
         TmpS := Text;
         if TmpS <> '' then
           if Key = VK_DELETE then
           begin
             FActionStack.FragmentDeleted(CaretPosition + 1, TmpS[CaretPosition
               + 1]);
             Delete(TmpS, CaretPosition + 1, 1);
           end
           else
           begin                       {BackSpace key was pressed}
             if CaretPosition > 0 then
               FActionStack.FragmentDeleted(CaretPosition,
                 TmpS[CaretPosition]);
             Delete(TmpS, CaretPosition, 1);
             CaretPosition := CaretPosition - 1;
           end;
         Text := TmpS;
       end;
     end;
   VK_INSERT:
     if Shift = [ssCtrl] then
       CopyToClipboard
     else
       if Shift = [ssShift] then
         PasteFromClipboard;
   Ord('c'),
     Ord('C'):
     if Shift = [ssCtrl] then
       CopyToClipboard;
   Ord('v'),
     Ord('V'):
     if Shift = [ssCtrl] then
       PasteFromClipboard;
   Ord('x'),
     Ord('X'):
     if Shift = [ssCtrl] then
       CutToClipboard;
   Ord('z'), Ord('Z'):
     if Shift = [ssCtrl] then
       UnDo;
 end;

 if Key in [VK_END, VK_HOME, VK_LEFT, VK_RIGHT] then
 begin
   if ssShift in Shift then
   begin
     if SelLength = 0 then
       FSelStart := OldCaretPosition;
     FSelStart := CaretPosition;
     FSelLength := FSelLength - (CaretPosition - OldCaretPosition);
   end
   else
     FSelLength := 0;
   Invalidate;
 end;
 UpdateCaretePosition;
end;

procedure TSeCustomEdit.KeyPress(var Key: Char);
begin
 inherited KeyPress(Key);

 if (Ord(Key) >= 32) and not ReadOnly then
   InsertChar(charToWideChar(Key));
end;

procedure TSeCustomEdit.MouseDown(Button: TMouseButton; Shift: TShiftState;
 x, y: integer);
begin
 inherited;
 if Button = mbLeft then
   FLMouseSelecting := true;

 SetFocus;

 if Button = mbLeft then
 begin
   CaretPosition := GetCoordinatePosition(x);
   SelLength := 0;
 end;
end;

procedure TSeCustomEdit.PaintBuffer;
var
 R                 : TRect;
begin
 R := GetEditRect;
 R.Bottom := FHeight - R.Top;

 PaintBackground(R, Canvas);

 if (Self is TSeCustomComboBox) and (TSeCustomComboBox(Self).ComboStyle =
   kcsDropDownList) then
   Exit;

 if Focused or not HideSelection then
   FillRect(Canvas, GetSelRect, clHighlight);

 PaintText;

 if Focused or not HideSelection then
   PaintSelectedText;
end;

procedure TSeCustomEdit.PaintBackground(Rect: TRect; Canvas: TCanvas);
begin
 FillRect(Canvas, Rect, Color);
end;

procedure TSeCustomEdit.PaintText;
var
 TmpRect           : TRect;
 CurChar           : integer;
 LPWCharWidth      : integer;
begin
 TmpRect := GetEditRect;

 if PasswordKind <> pkNone then
 begin
   LPWCharWidth := GetPasswordCharWidth;
   for CurChar := 0 to Length(Text) - FFirstVisibleChar + 1 - 1 do
     DrawPasswordChar(Rect(CurChar * LPWCharWidth + GetCharX(0),
       TmpRect.Top,
       (CurChar + 1) * LPWCharWidth + GetCharX(0),
       TmpRect.Bottom), false);
 end
 else
 begin
   Canvas.Font.Assign(ControlFont);
   DrawText(Canvas, Copy(Text, FFirstVisibleChar, Length(Text) -
     FFirstVisibleChar + 1),
     TmpRect, GetAlignmentFlags or DT_NOPREFIX);
 end;
end;

procedure TSeCustomEdit.UpdateFirstVisibleChar;
var
 LEditRect         : TRect;
begin
 if FFirstVisibleChar >= (FCaretPosition + 1) then
 begin
   FFirstVisibleChar := FCaretPosition;
   if FFirstVisibleChar < 1 then
     FFirstVisibleChar := 1;
 end
 else
 begin
   LEditRect := GetEditRect;

   if PasswordKind <> pkNone then
     while ((FCaretPosition - FFirstVisibleChar + 1) * GetPasswordCharWidth >
       LEditRect.Right - LEditRect.Left)
       and (FFirstVisibleChar < Length(Text)) do
       Inc(FFirstVisibleChar)
   else
   begin
     Canvas.Font.Assign(ControlFont);
     {while (TextWidth(Canvas, Copy(Text, FFirstVisibleChar, FCaretPosition -
       FFirstVisibleChar + 1), DT_NOPREFIX) > LEditRect.Right - LEditRect.Left)
       and (FFirstVisibleChar < Length(Text)) do
       Inc(FFirstVisibleChar); }
     while (Canvas.TextWidth(Copy(Text, FFirstVisibleChar, FCaretPosition -
       FFirstVisibleChar + 1)) > LEditRect.Right - LEditRect.Left)
       and (FFirstVisibleChar < Length(Text)) do
       Inc(FFirstVisibleChar);
   end;
 end;
 Invalidate;
end;

procedure TSeCustomEdit.MouseMove(Shift: TShiftState; x, y: integer);
var
 OldCaretPosition  : integer;
 TmpNewPosition    : integer;
begin
 inherited;
 if FLMouseSelecting then
 begin
   TmpNewPosition := GetCoordinatePosition(x);
   OldCaretPosition := CaretPosition;
   if (x > GetEditRect.Right) then
     CaretPosition := TmpNewPosition + 1
   else
     CaretPosition := TmpNewPosition;
   if SelLength = 0 then
     FSelStart := OldCaretPosition;
   FSelStart := CaretPosition;
   FSelLength := FSelLength - (CaretPosition - OldCaretPosition);
 end;
end;

procedure TSeCustomEdit.MouseUp(Button: TMouseButton; Shift: TShiftState;
 x, y: integer);
begin
 inherited;
 FLMouseSelecting := false;
end;

procedure TSeCustomEdit.CopyToClipboard;
var
 Data              : THandle;
 DataPtr           : Pointer;
 Size              : Cardinal;
 S                 : WideString;
begin
 if PasswordKind = pkNone then
   if Length(SelText) > 0 then
   begin
     S := SelText;
     if not IsWinNT then
     begin
       Clipboard.AsText := S;
     end
     else
     begin
       Size := Length(S);
       Data := GlobalAlloc(GMEM_MOVEABLE + GMEM_DDESHARE, 2 * Size + 2);
       try
         DataPtr := GlobalLock(Data);
         try
           Move(PWideChar(S)^, DataPtr^, 2 * Size + 2);
           Clipboard.SetAsHandle(CF_UNICODETEXT, Data);
         finally
           GlobalUnlock(Data);
         end;
       except
         GlobalFree(Data);
         raise;
       end;
     end;
   end;
end;

procedure TSeCustomEdit.PasteFromClipboard;
var
 Data              : THandle;
 Insertion         : WideString;
begin
 if ReadOnly then
   Exit;

 if Clipboard.HasFormat(CF_UNICODETEXT) then
 begin
   Data := Clipboard.GetAsHandle(CF_UNICODETEXT);
   try
     if Data <> 0 then
       Insertion := PWideChar(GlobalLock(Data));
   finally
     if Data <> 0 then
       GlobalUnlock(Data);
   end;
 end
 else
   Insertion := Clipboard.AsText;

 InsertText(Insertion);
end;

procedure TSeCustomEdit.PaintSelectedText;
var
 TmpRect           : TRect;
 CurChar           : integer;
 LPWCharWidth      : integer;
begin
 TmpRect := GetSelRect;

 if PasswordKind <> pkNone then
 begin
   LPWCharWidth := GetPasswordCharWidth;
   for CurChar := 0 to Length(GetVisibleSelText) - 1 do
     DrawPasswordChar(Rect(CurChar * LPWCharWidth + TmpRect.Left,
       TmpRect.Top,
       (CurChar + 1) * LPWCharWidth + TmpRect.Left,
       TmpRect.Bottom),
       true);
 end
 else
 begin
   Canvas.Font.Assign(ControlFont);
   Canvas.Font.Color := clHighlightText;
   DrawText(Canvas, GetVisibleSelText, TmpRect, GetAlignmentFlags or
     DT_NOPREFIX)
 end;
end;

function TSeCustomEdit.GetVisibleSelText: WideString;
begin
 if SelStart + 1 >= FFirstVisibleChar then
   Result := SelText
 else
   Result := Copy(SelText, FFirstVisibleChar - SelStart, Length(SelText) -
     (FFirstVisibleChar - SelStart) + 1);
end;

procedure TSeCustomEdit.BuildPopupMenu;
var
 TmpItem           : TSeCustomItem;
begin
 FPopupMenu := CreatePopupMenu(Self);

 if FPopupMenu = nil then
   Exit;

 TmpItem := CreatePopupMenuItem(FPopupMenu);
 with TmpItem do
 begin
   Caption := SEditUndo;
   OnClick := DoUndo;
 end;
 FPopupMenu.Items.Add(TmpItem);

 TmpItem := CreatePopupMenuItem(FPopupMenu);
 TmpItem.Caption := '-';
 FPopupMenu.Items.Add(TmpItem);

 TmpItem := CreatePopupMenuItem(FPopupMenu);
 with TmpItem do
 begin
   Caption := SEditCut;
   OnClick := DoCut;
 end;
 FPopupMenu.Items.Add(TmpItem);

 TmpItem := CreatePopupMenuItem(FPopupMenu);
 with TmpItem do
 begin
   Caption := SEditCopy;
   OnClick := DoCopy;
 end;
 FPopupMenu.Items.Add(TmpItem);

 TmpItem := CreatePopupMenuItem(FPopupMenu);
 with TmpItem do
 begin
   Caption := SEditPaste;
   OnClick := DoPaste;
 end;
 FPopupMenu.Items.Add(TmpItem);

 TmpItem := CreatePopupMenuItem(FPopupMenu);
 with TmpItem do
 begin
   Caption := SEditDelete;
   OnClick := DoDelete;
 end;
 FPopupMenu.Items.Add(TmpItem);

 TmpItem := CreatePopupMenuItem(FPopupMenu);
 TmpItem.Caption := '-';
 FPopupMenu.Items.Add(TmpItem);

 TmpItem := CreatePopupMenuItem(FPopupMenu);
 with TmpItem do
 begin
   Caption := SEditSelectAll;
   OnClick := DoSelectAll;
 end;
 FPopupMenu.Items.Add(TmpItem);
end;

function TSeCustomEdit.CreatePopupMenu(AOwner: TComponent): TSeCustomPopupMenu;
begin
 Result := TSeCustomPopupMenu.Create(AOwner);
end;

function TSeCustomEdit.CreatePopupMenuItem(AOwner: TComponent): TSeCustomItem;
begin
 Result := TSeCustomItem.Create(Self);
end;

procedure TSeCustomEdit.DoCut(Sender: TObject);
begin
 CutToClipboard;
end;

procedure TSeCustomEdit.DoCopy(Sender: TObject);
begin
 CopyToClipboard;
end;

procedure TSeCustomEdit.DoDelete(Sender: TObject);
begin
 ClearSelection;
end;

procedure TSeCustomEdit.DoPaste(Sender: TObject);
begin
 PasteFromClipboard;
end;

procedure TSeCustomEdit.UpdatePopupMenuItems;

 function SetItemEnabled(Event: TNotifyEvent; AEnabled: boolean):
     TSeCustomItem;
 var
   Item            : TSeCustomItem;
 begin
   Item := FPopupMenu.Items.FindItem(Integer(@Event), fkHandle);
   if Item <> nil then
     Item.Enabled := AEnabled;

   Result := Item;
 end;
var
 SelTextEmpty      : boolean;
begin
 if FPopupMenu = nil then
   BuildPopupMenu;

 SelTextEmpty := SelText <> '';

 SetItemEnabled(DoUndo, FActionStack.AtLeast(1) and not ReadOnly);
 SetItemEnabled(DoCut, SelTextEmpty and (not (PasswordKind <> pkNone)) and not
   ReadOnly);
 SetItemEnabled(DoCopy, SelTextEmpty and not (PasswordKind <> pkNone));
 SetItemEnabled(DoPaste, (ClipBoard.AsText <> '') and not ReadOnly);
 SetItemEnabled(DoDelete, SelTextEmpty and not ReadOnly);
 SetItemEnabled(DoSelectAll, SelText <> Text);

 { Set Properties }
 FPopupMenu.PopupMenuOptions := FContextMenuOptions;
end;

function TSeCustomEdit.GetNextWordBeging(StartPosition: integer): integer;
var
 SpaceFound,
   WordFound       : boolean;
begin
 Result := StartPosition;
 SpaceFound := false;
 WordFound := false;
 while (Result + 2 <= Length(Text)) and
   ((not ((Text[Result + 1] <> WideSpace) and SpaceFound))
   or not WordFound) do
 begin
   if Text[Result + 1] = WideSpace then
     SpaceFound := true;
   if Text[Result + 1] <> WideSpace then
   begin
     WordFound := true;
     SpaceFound := false;
   end;

   Result := Result + 1;
 end;
 if not SpaceFound then
   Result := Result + 1;
end;

function TSeCustomEdit.GetPrivWordBeging(StartPosition: integer): integer;
var
 WordFound         : boolean;
begin
 Result := StartPosition;
 WordFound := false;
 while (Result > 0) and
   ((Text[Result] <> WideSpace) or not WordFound) do
 begin
   if Text[Result] <> WideSpace then
     WordFound := true;
   Result := Result - 1;
 end;
end;

procedure TSeCustomEdit.ClearSelection;
var
 TmpS              : WideString;
begin
 if ReadOnly then
   Exit;

 TmpS := Text;
 FActionStack.FragmentDeleted(SelStart + 1,
   Copy(TmpS, SelStart + 1, SelLength));
 Delete(TmpS, SelStart + 1, SelLength);
 Text := TmpS;
 CaretPosition := SelStart;
 SelLength := 0;
end;

procedure TSeCustomEdit.CutToClipboard;
begin
 if PasswordKind = pkNone then
   CopyToClipboard;
 ClearSelection;
end;

procedure TSeCustomEdit.SelectAll;
begin
 SetCaretPosition(Length(Text));
 SelStart := 0;
 SelLength := Length(Text);
 Invalidate;
end;

procedure TSeCustomEdit.DoSelectAll(Sender: TObject);
begin
 SelectAll;
end;

procedure TSeCustomEdit.DrawPasswordChar(SymbolRect: TRect; Selected: boolean);
var
 R                 : TRect;
 Rgn               : HRgn;
begin
 { !!! Don't forget include clipping rountines
       Char symbol image must not extend out of EditRect}

 Rgn := CreateRectRgn(SymbolRect.Left, SymbolRect.Top, SymbolRect.Right,
   SymbolRect.Bottom);
 try
   SelectClipRgn(Canvas.Handle, Rgn);

   Canvas.Font.Assign(ControlFont);
   if Selected then
     Canvas.Font.Color := clHighlightText;

   R := SymbolRect;
   InflateRect(R, -2, -3);

   case FPasswordKind of
     pkChar: DrawText(Canvas, FPasswordChar, SymbolRect, DT_LEFT or
         DT_NOPREFIX);
     pkRect: FillRect(Canvas, R, Canvas.Font.Color);
     pkRoundRect: FillRoundRect(Canvas, R, 2, Canvas.Font.Color);
     pkCircle:
       begin
         R := Rect(0, 0, RectWidth(R), RectWidth(R));
         RectCenter(R, SymbolRect);

         FillRoundRect(Canvas, R, RectWidth(R) div 2 + 1, Canvas.Font.Color);
       end;
     pkTriangle:
       begin
         R := Rect(0, 0, RectWidth(R), RectWidth(R));
         if not Odd(RectWidth(R)) then
           R.Right := R.Right + 1;
         RectCenter(R, SymbolRect);

         Canvas.Brush.Color := Canvas.Font.Color;
         Canvas.Polygon([
           Point(R.Left + RectWidth(R) div 2 + 1, R.Top),
             Point(R.Right, R.Bottom),
             Point(R.Left, R.Bottom)
             ]);
       end;
   end;
 finally
   SelectClipRgn(Canvas.Handle, 0);
   DeleteObject(Rgn);
 end;
end;

function TSeCustomEdit.CanAutoSize(var NewWidth, NewHeight: Integer): Boolean;
begin
 Result := True;
 Canvas.Font.Assign(ControlFont);
 NewHeight := TextHeight(Canvas, 'Pq') + GetEditRect.Top * 2;
end;

procedure TSeCustomEdit.SelectWord;
begin
 SelStart := GetPrivWordBeging(CaretPosition);
 ;
 SelLength := GetNextWordBeging(SelStart) - SelStart;
 CaretPosition := SelStart + SelLength;
end;

procedure TSeCustomEdit.UpdateCarete;
begin
 Canvas.Font.Assign(ControlFont);
 CreateCaret(Handle, 0, 0, Canvas.TextHeight('Pq'));
 CaretPosition := FCaretPosition;
 ShowCaret;
end;

procedure TSeCustomEdit.HideCaret;
begin
 Windows.HideCaret(Handle);
end;

procedure TSeCustomEdit.ShowCaret;
begin
 Windows.ShowCaret(Handle);
end;

function TSeCustomEdit.GetPasswordCharWidth: integer;
begin
 Canvas.Font.Assign(ControlFont);

 case FPasswordKind of
   pkChar: Result := Canvas.TextWidth(FPasswordChar);
   pkRect, pkRoundRect, pkCircle, pkTriangle: Result := Canvas.TextWidth( 'W');
 else
   Result := 10;
 end;

 if Result = 0 then
   Result := 1;
end;

procedure TSeCustomEdit.Change;
begin
 inherited Changed;

 if Enabled and HandleAllocated then
   SetCaretPosition(CaretPosition);

 if Assigned(FOnChange) then
   FOnChange(Self);
end;

procedure TSeCustomEdit.WMImeStartComposition(var Message: TMessage);
var
 IMC               : HIMC;
 LogFont           : TLogFont;
 CF                : TCompositionForm;
begin
 inherited;

 IMC := ImmGetContext(Handle);
 if IMC <> 0 then
 begin
   if Assigned(Font) then
   begin
     GetObject(Font.Handle, SizeOf(TLogFont), @LogFont);
     ImmSetCompositionFont(IMC, @LogFont);
   end;

   CF.dwStyle := CFS_RECT;
   CF.rcArea := GetEditRect;
   CF.ptCurrentPos := Point(GetCharX(FCaretPosition), CF.rcArea.Top);
   ImmSetCompositionWindow(IMC, @CF);
   ImmReleaseContext(Handle, IMC);
 end;
end;

procedure TSeCustomEdit.WMImeComposition(var Msg: TMessage);
var
 IMC               : HIMC;
 Buff              : WideString;
 i                 : integer;
begin
 if Msg.lParam and GCS_RESULTSTR <> 0 then
 begin
   IMC := ImmGetContext(Handle);
   if IMC <> 0 then
   begin
     try
       { Get the result string }
       SetLength(Buff, ImmGetCompositionStringW(IMC, GCS_RESULTSTR, nil, 0) div
         SizeOf(WideChar));
       ImmGetCompositionStringW(IMC, GCS_RESULTSTR, PWideChar(Buff),
         Length(Buff) * SizeOf(WideChar));
     finally
       ImmReleaseContext(Handle, IMC);
     end;

     { Insert char messages for each char in string }
     for i := 1 to Length(Buff) do
       InsertChar(Buff[i]);

     Msg.Result := 0;
     Exit;
   end;
 end;

 inherited;
end;

procedure TSeCustomEdit.WMGetDlgCode(var Msg: TWMGetDlgCode);
begin
 inherited;
 Msg.Result := dlgc_WantArrows or DLGC_WANTCHARS;
end;

procedure TSeCustomEdit.WMCut(var Message: TMessage);
begin
 CutToClipboard;
end;

procedure TSeCustomEdit.WMCopy(var Message: TMessage);
begin
 CopyToClipboard;
end;

procedure TSeCustomEdit.WMPaste(var Message: TMessage);
begin
 PasteFromClipboard;
end;

procedure TSeCustomEdit.WMContexMenu(var Message: TMessage);
var
 LForm             : TCustomForm;
begin
 inherited;

 if csDesigning in ComponentState then
   Exit;

 UpdatePopupMenuItems;

 LForm := GetParentForm(Self);
 if LForm <> nil then
   LForm.SendCancelMode(nil);

 FPopupMenu.PopupComponent := Self;

 with Message do
   FPopUpMenu.Popup(LParamLo, LParamHi);
end;

procedure TSeCustomEdit.WMLButtonDblClk(var Message: TWMLButtonDblClk);
begin
 inherited;
 FLMouseSelecting := false;
 SelectWord;
end;

procedure TSeCustomEdit.CMFontChanged(var Message: TMessage);
begin
 inherited;
 ControlFont.Assign(Font);
 AdjustSize;
 UpdateCarete;
end;

procedure TSeCustomEdit.SetFont(Value: TFont);
begin
 inherited Font := Value;
 ControlFont.Assign(Value);
 AdjustSize;
end;

function TSeCustomEdit.GetText: WideString;
begin
 Result := FText;
end;

procedure TSeCustomEdit.SetText(const Value: WideString);
var
 TmpS              : WideString;
 LOldText          : WideString;
begin
 if not ValidText(Value) then
   Exit;

 TmpS := Value;
 LOldText := Text;

 if (Value <> '') and (CharCase <> ecNormal) then
   case CharCase of
     ecUpperCase: FText := AnsiUpperCase(TmpS);
     ecLowerCase: FText := AnsiLowerCase(TmpS);
   end
 else
   FText := TmpS;

 Invalidate;

 if Text <> LOldText then
   Change;
end;

procedure TSeCustomEdit.SetCaretPosition(const Value: integer);
begin
 if Value < 0 then
   FCaretPosition := 0
 else
   if Value > Length(Text) then
     FCaretPosition := Length(Text)
   else
     FCaretPosition := Value;

 UpdateFirstVisibleChar;

 if SelLength <= 0 then
   FSelStart := Value;

 if Focused then
   SetCaretPos(GetCharX(FCaretPosition), GetEditRect.Top);
end;

procedure TSeCustomEdit.SetPasswordChar(const Value: WideChar);
begin
 if FPasswordChar <> Value then
 begin
   if Value <> WideChar(#0) then
     FPasswordKind := pkChar;

   FPasswordChar := Value;
   Invalidate;
   CaretPosition := CaretPosition;     //Update caret position
 end;
end;

procedure TSeCustomEdit.SetSelLength(const Value: integer);
begin
 if FSelLength <> Value then
 begin
   FSelLength := Value;
   Invalidate;
 end;
end;

procedure TSeCustomEdit.SetSelStart(const Value: integer);
begin
 if FSelStart <> Value then
 begin
   SelLength := 0;
   FSelStart := Value;
   CaretPosition := FSelStart;
   Invalidate;
 end;
end;

procedure TSeCustomEdit.SetAutoSelect(const Value: boolean);
begin
 if FAutoSelect <> Value then
   FAutoSelect := Value;
end;

function TSeCustomEdit.GetSelStart: integer;
begin
 if FSelLength > 0 then
   Result := FSelStart
 else
   if FSelLength < 0 then
     Result := FSelStart + FSelLength
   else
     Result := CaretPosition;
end;

function TSeCustomEdit.GetSelRect: TRect;
begin
 Result := GetEditRect;
 Result.Left := GetCharX(SelStart);
 Result.Right := GetCharX(SelStart + SelLength);
 IntersectRect(Result, Result, GetEditRect);
end;

function TSeCustomEdit.GetSelLength: integer;
begin
 Result := Abs(FSelLength);
end;

function TSeCustomEdit.GetSelText: WideString;
begin
 Result := Copy(Text, SelStart + 1, SelLength);
end;

procedure TSeCustomEdit.SetCharCase(const Value: TEditCharCase);
var
 TmpS              : WideString;
begin
 if FCharCase <> Value then
 begin
   FCharCase := Value;
   if Text <> '' then
   begin
     TmpS := Text;
     case Value of
       ecUpperCase: Text := AnsiUpperCase(TmpS);
       ecLowerCase: Text := AnsiLowerCase(TmpS);
     end;
   end;
 end;
end;

procedure TSeCustomEdit.SetHideSelection(const Value: Boolean);
begin
 if FHideSelection <> Value then
 begin
   FHideSelection := Value;
   Invalidate;
 end;
end;

procedure TSeCustomEdit.SetMaxLength(const Value: Integer);
begin
 if FMaxLength <> Value then
 begin
   FMaxLength := Value;
 end;
end;

procedure TSeCustomEdit.SetCursor(const Value: TCursor);
begin
 if Value = crDefault then
   inherited Cursor := crIBeam
 else
   inherited Cursor := Value;
end;

function TSeCustomEdit.ValidText(NewText: WideString): boolean;
begin
 Result := true;
end;

procedure TSeCustomEdit.SetTextAlignment(const Value: TAlignment);
begin
 if FTextAlignment <> Value then
 begin
   FTextAlignment := Value;
   Invalidate;
 end;
end;

procedure TSeCustomEdit.UpdateCaretePosition;
begin
 SetCaretPosition(CaretPosition);
end;

procedure TSeCustomEdit.InsertText(AText: WideString);
var
 TmpS              : WideString;
begin
 if ReadOnly then
   Exit;

 TmpS := Text;
 FActionStack.FragmentDeleted(SelStart + 1, Copy(TmpS, SelStart + 1,
   SelLength));
 Delete(TmpS, SelStart + 1, SelLength);
 FActionStack.FragmentInserted(SelStart + 1, Length(AText), SelLength <> 0);
 Insert(AText, TmpS, SelStart + 1);
 if (MaxLength <= 0) or (Length(TmpS) <= MaxLength) then
 begin
   Text := TmpS;
   CaretPosition := SelStart + Length(AText);
 end;
 SelLength := 0;
end;

procedure TSeCustomEdit.InsertChar(Ch: WideChar);
begin
 if ReadOnly then
   Exit;

 InsertText(Ch);
end;

procedure TSeCustomEdit.InsertAfter(Position: integer; S: WideString;
 Selected: boolean);
var
 TmpS              : WideString;
 Insertion         : WideString;
begin
 TmpS := Text;
 Insertion := S;
 if MaxLength > 0 then
   Insertion := Copy(Insertion, 1, MaxLength - Length(TmpS));
 Insert(Insertion, TmpS, Position + 1);
 Text := TmpS;
 if Selected then
 begin
   SelStart := Position;
   SelLength := Length(Insertion);
   CaretPosition := SelStart + SelLength;
 end;
end;

procedure TSeCustomEdit.DeleteFrom(Position, Length: integer; MoveCaret:
 boolean);
var
 TmpS              : WideString;
begin
 TmpS := Text;
 Delete(TmpS, Position, Length);
 Text := TmpS;
 if MoveCaret then
 begin
   SelLength := 0;
   SelStart := Position - 1;
 end;
end;

procedure TSeCustomEdit.DoUndo(Sender: TObject);
begin
 UnDo;
end;

procedure TSeCustomEdit.WMUnDo(var Message: TMessage);
begin
 UnDo;
end;

procedure TSeCustomEdit.UnDo;
begin
 FActionStack.RollBackAction;
end;

procedure TSeCustomEdit.SetPasswordKind(const Value: TPasswordKind);
begin
 if FPasswordKind <> Value then
 begin
   FPasswordKind := Value;
   Invalidate;
 end;
end;

procedure TSeCustomEdit.SetPopupMenuBlendValue(const Value: integer);
begin
 FPopupMenuBlendValue := Value;
end;

procedure TSeCustomEdit.SetPopupMenuDropShadow(const Value: boolean);
begin
 FPopupMenuDropShadow := Value;
end;

procedure TSeCustomEdit.SetPopupMenuShadowWidth(const Value: integer);
begin
 FPopupMenuShadowWidth := Value;
end;

procedure TSeCustomEdit.SetPopupMenuShowAnimation(
 const Value: TSeAnimationRec);
begin
 FPopupMenuShowAnimation := Value;
end;

procedure TSeCustomEdit.SetPopupMenuShowAnimationTime(
 const Value: integer);
begin
 FPopupMenuShowAnimationTime := Value;
end;

procedure TSeCustomEdit.CMTextChanged(var Msg: TMessage);
begin
 inherited;
 FText := inherited Text;
 SelLength := 0;
 Invalidate;
end;

procedure TSeCustomEdit.SetPopupMenuBlend(const Value: boolean);
begin
 FPopupMenuBlend := Value;
end;

procedure TSeCustomEdit.Clear;
begin
 Text := '';
end;

procedure TSeCustomEdit.BorderChanged;
begin
 inherited;
 AdjustSize;
end;

procedure TSeCustomEdit.SetContextMenuOptions(const Value: TSePopupMenuOptions);
begin
 FContextMenuOptions.Assign(Value);
end;

procedure TSeCustomEdit.CMEnabledChanged(var Msg: TMessage);
begin
 if HandleAllocated and not (csDesigning in ComponentState) then
   EnableWindow(Handle, Enabled);
 Invalidate;
end;
总共用了我一个半小时的时间去追踪,真TMD。为什么双字节冲突这么明显的Bug开发人员都不去解决?  
  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
<;p style=";color:#666666;";&gt; <;span style=";font-size:14px;";&gt;本门课程重实战,将基础知识拆解到项目里,让你在项目情境里学知识。<;/span&gt; <;/p&gt; <;p style=";color:#666666;";&gt; <;span style=";font-size:14px;";&gt;这样学习方式能让你保持兴趣、充满动力,时刻知道学东西能用在哪、能怎么用。<;/span&gt; <;/p&gt; <;p style=";color:#666666;";&gt; <;span style=";font-size:14px;";&gt;平时不明白知识点,放在项目里去理解就恍然大悟了。<;/span&gt; <;/p&gt; <;p style=";color:#666666;";&gt; <;span&gt;<;/span&gt; <;/p&gt; <;p style=";color:#666666;";&gt; <;span style=";font-size:14px;";&gt; ;<;/span&gt; <;/p&gt; <;p style=";color:#666666;";&gt; <;span style=";color:#FF0000;font-size:14px;";&gt;<;strong&gt;一、融汇贯通<;/strong&gt;<;/span&gt; <;/p&gt; <;p style=";color:#666666;";&gt; <;span style=";font-size:14px;";&gt;本视频采用了前后端分离开发模式,前端使用Vue.js+Element UI实现了Web页面呈现,后端使用Python Django框架实现了数据访问接口,前端通过Axios访问后端接口获得数据。在学习完本章节后,真正理解前后端各自承担工作。<;/span&gt; <;/p&gt; <;p style=";color:#666666;";&gt; <;span style=";font-size:14px;";&gt; ;<;/span&gt; <;/p&gt; <;p style=";color:#666666;";&gt; <;span style=";color:#FF0000;font-size:14px;";&gt;<;strong&gt;二、贴近实战<;/strong&gt;<;/span&gt; <;/p&gt; <;p style=";color:#666666;";&gt; <;span style=";font-size:14px;";&gt;本系列课程为练手项目实战:学生管理系统v4.0开发,项目包含了如下几个内容:项目总体介绍、基本功能演示、Vuejs初始化、Element UI使用、在Django中实现针对数据增删改查接口、在Vuejs中实现前端增删改查调用、实现文件上传、实现表格分页、实现导出数据到Excel、实现通过Excel导入数据、实现针对表格批量化操作等等,所有功能都通过演示完成、贴近了实战<;/span&gt; <;/p&gt; <;p style=";color:#666666;";&gt; <;span style=";font-size:14px;";&gt; ;<;/span&gt; <;/p&gt; <;p style=";color:#666666;";&gt; <;span style=";color:#FF0000;font-size:14px;";&gt;<;strong&gt;三、课程亮点<;/strong&gt;<;/span&gt; <;/p&gt; <;p style=";color:#666666;";&gt; <;span style=";font-size:14px;";&gt;在本案例中,最大亮点在于前后端做了分离,真正理解前后端各自承担工作。前端如何和后端交互<;/span&gt; <;/p&gt; <;p style=";color:#666666;";&gt; <;span style=";font-size:14px;";&gt; ;<;/span&gt; <;/p&gt; <;p style=";color:#666666;";&gt; <;span style=";color:#FF0000;font-size:14px;";&gt;<;strong&gt;适合人群:<;/strong&gt;<;/span&gt; <;/p&gt; <;p style=";color:#666666;";&gt; <;span style=";font-size:14px;";&gt;1、有Python语言基础、web前端基础,想要深入学习Python Web框架朋友;<;/span&gt; <;/p&gt; <;p style=";color:#666666;";&gt; <;span style=";font-size:14px;";&gt;2、有Django基础,但是想学习企业级项目实战朋友;<;/span&gt; <;/p&gt; <;p style=";color:#666666;";&gt; <;span style=";font-size:14px;";&gt;3、有MySQL数据库基础朋友<;/span&gt; <;/p&gt; <;p style=";color:#666666;";&gt; <;span style=";font-size:14px;";&gt; ;<;/span&gt; <;/p&gt; <;p style=";color:#666666;";&gt; <;span style=";font-size:14px;";&gt;<;img alt=";"; src=";https://img-bss.csdnimg.cn/202009070752197496.png"; /&gt;<;br /&gt; <;/span&gt; <;/p&gt; <;p style=";color:#666666;";&gt; <;span style=";font-size:14px;";&gt;<;br /&gt; <;/span&gt; <;/p&gt;
<;div style=";color:rgba(0,0,0,.75);";&gt; <;span style=";color:#4d4d4d;";&gt; <;/span&gt; <;div style=";color:rgba(0,0,0,.75);";&gt; <;span style=";color:#4d4d4d;";&gt; <;/span&gt; <;div style=";color:rgba(0,0,0,.75);";&gt; <;div style=";color:rgba(0,0,0,.75);";&gt; <;span style=";color:#4d4d4d;";&gt;当前课程中商城项目实战源码是我发布在 GitHub 上开源项目 newbee-mall (新蜂商城),目前已有 6300 多个 star,<;/span&gt;<;span style=";color:#4d4d4d;";&gt;本课程是一个 Spring Boot 技术栈实战类课程,课程共分为 3 大部分,前面两个部分为基础环境准备和相关概念介绍,第三个部分是 Spring Boot 商城项目功能讲解,让大家实际操作并实践上手一个大型线上商城项目,并学习到一定开发经验以及其中开发技巧。<;br /&gt; 商城项目所涉及功能结构图整理如下:<;br /&gt; <;/span&gt; <;/div&gt; <;div style=";color:rgba(0,0,0,.75);";&gt;  ; <;/div&gt; <;div style=";color:rgba(0,0,0,.75);";&gt; <;p style=";color:#4d4d4d;";&gt; <;img alt=";modules"; src=";https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3N0b3JlL25ld2JlZS1tYWxsLXMucG5n?x-oss-process=image/format,png"; /&gt; <;/p&gt; <;/div&gt; <;p style=";color:rgba(0,0,0,.75);";&gt; <;strong&gt;<;span style=";color:#e53333;";&gt;课程特色<;/span&gt;<;/strong&gt; <;/p&gt; <;p style=";color:rgba(0,0,0,.75);";&gt;  ; <;/p&gt; <;div style=";color:rgba(0,0,0,.75);";&gt;  ; <;/div&gt; <;div style=";color:rgba(0,0,0,.75);";&gt; <;ul&gt; <;li&gt; 对新手开发者十分友好,无需复杂操作步骤,仅需 2 秒就可以启动这个完整商城项目 <;/li&gt; <;li&gt; 最终实战项目是一个企业级别 Spring Boot 大型项目,对于各个阶段 Java 开发者都是极佳选择 <;/li&gt; <;li&gt; 实践项目页面美观且实用,交互效果完美 <;/li&gt; <;li&gt; 教程详细开发教程详细完整、文档资源齐全 <;/li&gt; <;li&gt; 代码+讲解+演示网站全方位保证,向 Hello World 教程说拜拜 <;/li&gt; <;li&gt; 技术栈新颖且知识点丰富,学习后可以提升大家对于知识理解和掌握,可以进一步提升你市场竞争力 <;/li&gt; <;/ul&gt; <;/div&gt; <;p style=";color:rgba(0,0,0,.75);";&gt;  ; <;/p&gt; <;p style=";color:rgba(0,0,0,.75);";&gt; <;span style=";color:#e53333;";&gt;课程预览<;/span&gt; <;/p&gt; <;p style=";color:rgba(0,0,0,.75);";&gt;  ; <;/p&gt; <;div style=";color:rgba(0,0,0,.75);";&gt;  ; <;/div&gt; <;div style=";color:rgba(0,0,0,.75);";&gt; <;p style=";color:#4d4d4d;";&gt; 以下为商城项目页面和功能展示,分别为: <;/p&gt; <;/div&gt; <;div style=";color:rgba(0,0,0,.75);";&gt; <;ul&gt; <;li&gt; 商城首页 1<;br /&gt; <;img alt=";"; src=";https://img-bss.csdnimg.cn/202103050347585499.gif"; /&gt; <;/li&gt; <;li&gt; 商城首页 2<;br /&gt; <;img alt=";"; src=";https://img-bss.csdn.net/202005181054413605.png"; /&gt; <;/li&gt; <;li&gt;  ; <;/li&gt; <;li&gt; 购物车<;br /&gt; <;img alt=";cart"; src=";https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3Byb2R1Y3QvY2FydC5wbmc?x-oss-process=image/format,png"; /&gt; <;/li&gt; <;li&gt; 订单结算<;br /&gt; <;img alt=";settle"; src=";https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3Byb2R1Y3Qvc2V0dGxlLnBuZw?x-oss-process=image/format,png"; /&gt; <;/li&gt; <;li&gt; 订单列表<;br /&gt; <;img alt=";orders"; src=";https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3Byb2R1Y3Qvb3JkZXJzLnBuZw?x-oss-process=image/format,png"; /&gt; <;/li&gt; <;li&gt; 支付页面<;br /&gt; <;img alt=";"; src=";https://img-bss.csdn.net/201909280301493716.jpg"; /&gt; <;/li&gt; <;li&gt; 后台管理系统登录页<;br /&gt; <;img alt=";login"; src=";https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3Byb2R1Y3QvbWFuYWdlLWxvZ2luLnBuZw?x-oss-process=image/format,png"; /&gt; <;/li&gt; <;li&gt; 商品管理<;br /&gt; <;img alt=";goods"; src=";https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3Byb2R1Y3QvbWFuYWdlLWdvb2RzLnBuZw?x-oss-process=image/format,png"; /&gt; <;/li&gt; <;li&gt; 商品编辑<;br /&gt; <;img alt=";"; src=";https://img-bss.csdnimg.cn/202103050348242799.png"; /&gt; <;/li&gt; <;/ul&gt; <;/div&gt; <;/div&gt; <;/div&gt; <;/div&gt;
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值