作成日: 2026/03/17 最終更新日: 2026/03/17
文書種別
制限事項
状況
回避方法あり
詳細
FlexGridで下記の例のような実装を行い、列ヘッダーの最下段にある行方向に結合したセルの列を固定すると、固定される列の位置がずれてしまいます。
(例)
・getMergedRangeを拡張してヘッダーセルの結合/セル結合を実装
・allowPinning = 'ColumnRange'を設定し、列を固定できるようにする
・2段構成のヘッダーで、最下段に行方向(水平方向)の結合セルがある
こちらのサンプルに、Date列とAmount列のヘッダーセルを結合する処理を実装したのち、「grid.allowPinning = "ColumnRange";」を追加して、Date列をピンで固定後、横スクロールを行うことで再現します。
回避方法
グリッドの pinningColumn イベント、および pinnedColumn イベントを処理し、固定する列数を結合されたヘッダー範囲全体をカバーするように変更します。
以下は、こちらのサンプルで現象が再現するように実装した場合のサンプルコードです。コメントで「WORKAROUND」と記載されている部分が回避方法として追加されたコードです。
【CSS】
.wj-flexgrid {
max-height: 200px;
width: 600px;
}
/* ===== WORKAROUND: 固定列の z-index ===== */
.wj-frozen-col-left {
z-index: 2 !important;
}
/* ======================================= */
【JavaScript】
// テストデータ生成
var count = 50;
var countries = 'US,Germany,UK,Japan,Italy,Greece'.split(',');
var data = [];
for (var i = 0; i < count; i++) {
data.push({
id: i,
country: countries[i % countries.length],
date: new Date(2014, i % 12, i % 28),
amount: Math.random() * 10000,
active: i % 4 === 0
});
}
// グリッド生成
var grid = new wijmo.grid.FlexGrid('#grid');
grid.itemsSource = data;
grid.allowPinning = "ColumnRange";
// 列ヘッダに行追加
// ===== WORKAROUND: 中央揃えのために wj-colgroup クラスを追加する =====
var headerRow = new wijmo.grid.Row();
headerRow.cssClassAll = 'wj-colgroup wj-align-center';
// ===================================================================
grid.columnHeaders.rows.insert(0, headerRow);
// ヘッダーセルデータを設定する
for (var i = 0; i < grid.columns.length; i++) {
if (i == 0 || i == 4) {
grid.columnHeaders.setCellData(0, i, grid.columnHeaders.getCellData(1, i));
}
}
for (var i = 1; i < 4; i++) {
grid.columnHeaders.setCellData(0, i, "結合");
}
// MergeManagerを作成
var mm = new wijmo.grid.MergeManager();
// 結合するセルの範囲
var ranges = [
new wijmo.grid.CellRange(0, 2, 1, 2),
new wijmo.grid.CellRange(2, 2, 3, 2)
];
// ===== WORKAROUND =============================================================
// ヘッダーの水平方向のマージ定義
// getMergedRange(切り取り用)とpinnedColumn(展開用)の両方で使用されます
var headerHMerges = [
{ row: 0, col: 1, row2: 0, col2: 3 }, // "結合" 1~3列目
{ row: 1, col: 2, row2: 1, col2: 3 } // "date+amount" 2~3列目
];
// 固定境界で水平マージ範囲をクリップします
function clipAtFrozenBoundary(panel, c, mergeCol, mergeCol2, mergeRow, mergeRow2) {
var cols = panel.columns;
var frozenCols = cols.frozen;
if (frozenCols > 0 && mergeCol < frozenCols && mergeCol2 >= frozenCols) {
// マージ範囲が固定境界を越える - クリッピングが必要
if (cols.isFrozen(c)) {
// 現在の列は固定されています - 右端を固定された境界にクリップします
mergeCol2 = frozenCols - 1;
} else {
// 現在の列は固定されていません - 左端を固定された境界にクリップします
mergeCol = frozenCols;
}
if (mergeCol > mergeCol2) {
return null; // クリッピング後の無効な範囲
}
}
return new wijmo.grid.CellRange(mergeRow, mergeCol, mergeRow2, mergeCol2);
}
// ==============================================================================
// MergeManagerのgetMergedRangeメソッドをオーバーライド
mm.getMergedRange = function (panel, r, c, clip) {
if (panel.cellType == wijmo.grid.CellType.Cell) {
for (var i = 0; i < ranges.length; i++) {
if (ranges[i].contains(r, c)) {
return ranges[i];
}
}
}
else if (panel.cellType == wijmo.grid.CellType.ColumnHeader) {
if (c == 0 || c == 4) {
return new wijmo.grid.CellRange(0, c, 1, c);
}
// if (r == 0) {
// if (c == 1 || c == 2 || c == 3) {
// return new wijmo.grid.CellRange(0, 1, 0, 3);
// }
// }
// if (r == 1) {
// if (c == 2 || c == 3) {
// return new wijmo.grid.CellRange(1, 2, 1, 3);
// }
// }
// ===== WORKAROUND: 固定境界のクリッピングを考慮して水平マージを確認 =====
for (var i = 0; i < headerHMerges.length; i++) {
var m = headerHMerges[i];
if (r == m.row && c >= m.col && c <= m.col2) {
return clipAtFrozenBoundary(panel, c, m.col, m.col2, m.row, m.row2);
}
}
// ====================================================================
}
else if (panel.cellType == wijmo.grid.CellType.TopLeft) {
return new wijmo.grid.CellRange(0, 0, 1, 0);
}
return null;
};
// mergeManagerプロパティに割り当て
grid.mergeManager = mm;
// ===== WORKAROUND ==========================================================
// グリッドのクライアント幅を超える固定列のサイズを変更します
function resizeFrozenColumns(grid) {
var cols = grid.columns;
var frozen = cols.frozen;
if (frozen <= 0) return;
var totalWidth = 0;
for (var i = 0; i < frozen; i++) {
totalWidth += cols[i].renderSize;
}
var clientWidth = grid.clientSize.width;
if (totalWidth + 5 > clientWidth) {
var colWidth = Math.round((clientWidth - frozen * 5) / frozen);
if (cols.length <= frozen) colWidth = Math.round(clientWidth / frozen);
for (var i = 0; i < frozen; i++) {
cols[i].width = colWidth;
}
}
}
// 固定する列数をマージされたヘッダー範囲全体をカバーするようにを拡張します
// 固定する列数が増える場合にのみ拡張します(固定を解除したり、列数を減らしたりする場合は拡張しません)。
// 最下段のヘッダー行(ピンアイコンがある行)のマージのみを考慮し、0行目の「結合」セルのマージによって
// すべての列が固定されるのを防ぎます。
var _prevFrozen = 0;
grid.pinningColumn.addHandler(function (s, e) {
_prevFrozen = s.columns.frozen;
});
grid.pinnedColumn.addHandler(function (s, e) {
var cols = s.columns;
var frozen = cols.frozen;
var pinCol = e.col;
var bottomRow = s.columnHeaders.rows.length - 1;
// ケース1:frozen(固定列数)が減る場合 → ピンが外れた状態として扱う
// サブケース1a: 全列が固定された状態からの解除
if (frozen > 0 && frozen < _prevFrozen && _prevFrozen == cols.length) {
// 固定を維持するべき最も右側の列を見つける
// デフォルト:クリックした列を含め、そこまでの列を固定する
var targetFrozen = pinCol + 1;
// クリックした列が最下段のマージ範囲内にあるかどうかを確認する
for (var i = 0; i < headerHMerges.length; i++) {
var m = headerHMerges[i];
if (m.row == bottomRow && pinCol >= m.col && pinCol <= m.col2) {
// クリックした列がマージ範囲内にある → マージ範囲の右端まで固定する
targetFrozen = m.col2 + 1;
break;
}
}
s.columns.frozen = targetFrozen;
return;
}
// サブケース1b:固定列数が拡張された状態(Date列の固定列数が3→4に拡張された状態)からの解除
if (frozen > 0 && frozen < _prevFrozen) {
for (var i = 0; i < headerHMerges.length; i++) {
var m = headerHMerges[i];
if (m.row == bottomRow
&& pinCol >= m.col && pinCol <= m.col2
&& m.col2 + 1 == _prevFrozen) {
s.columns.frozen = 0;
return;
}
}
}
// ケース2:frozen(固定列数)が増加している場合 → マージされた範囲全体をカバーするように拡張
if (frozen <= 0 || frozen <= _prevFrozen) return;
var expanded = true;
while (expanded) {
expanded = false;
frozen = s.columns.frozen;
for (var i = 0; i < headerHMerges.length; i++) {
var m = headerHMerges[i];
if (m.row == bottomRow
&& pinCol >= m.col && pinCol <= m.col2
&& m.col < frozen && m.col2 >= frozen) {
s.columns.frozen = m.col2 + 1;
expanded = true;
break;
}
}
}
// 拡張後、固定列のサイズがグリッド幅を超える場合は、列のサイズを変更します
resizeFrozenColumns(s);
});
// ===================================================================