TL;DR — Un gráfico no guarda datos — guarda una referencia a un Range. El
.Valuesde cada serie es una fórmula del tipo=Sheet1!$B$2:$B$13, así que el gráfico es solo tan correcto como esa referencia. Crea un gráfico incrustado conws.ChartObjects.Add(left, top, width, height), coge su.Chart, fija.ChartType, y enlaza los datos de una sola vez con.SetSourceData Source:=Range(...). Las dos cosas que muerden: los gráficos incrustados viven enChartObjects(no enCharts, que son hojas de gráfico), y reejecutar la macro apila gráficos duplicados a menos que borres antes los viejos.
Dim co As ChartObject, ch As Chart
Set co = Worksheets("Report").ChartObjects.Add( _
Left:=300, Top:=20, Width:=420, Height:=260)
Set ch = co.Chart ' el ChartObject es el marco; .Chart es el grafico
ch.ChartType = xlColumnClustered
ch.SetSourceData Source:=Worksheets("Data").Range("A1:B13") ' enlaza todo el rango de una vez
ch.HasTitle = True
ch.ChartTitle.Text = "Monthly Sales"
Hacer gráficos en VBA parece fácil hasta la segunda ejecución, cuando te encuentras dos gráficos, o la primera, cuando las barras dibujan la columna equivocada. Las dos cosas vienen del mismo malentendido, y la solución para ambas es la misma idea: un gráfico no contiene tus datos — apunta a ellos. Cada serie guarda una referencia a un rango, escrita como una fórmula. Mueve los datos, inserta una fila, reconstruye el origen en otro sitio, y el gráfico sigue apuntando adonde se le dijo, dibujando fielmente las celdas equivocadas. En cuanto retienes «un gráfico es una vista ligada a un Range», la API deja de ser engorrosa y los dos bugs clásicos se vuelven obvios.
Lo que aprenderás
- El modelo mental — un gráfico guarda una referencia a un Range, no una copia de los datos
- Los dos contenedores: los
ChartObjectsincrustados frente a las hojas de gráfico enCharts - Crear un gráfico y enlazar los datos de una sola vez con
SetSourceData - Añadir y controlar series a mano con
SeriesCollection - La trampa del gráfico duplicado al reejecutar, y cómo limpiar los gráficos viejos
- Enlazar el origen a una Table para que el gráfico siga a los datos
El modelo mental: un gráfico apunta a un Range
Cuando haces un gráfico de un rango, Excel no copia los números dentro del gráfico. Almacena, para cada serie, una fórmula que referencia el rango — igual que una fórmula de celda referencia otras celdas. Puedes leerla de vuelta:
Debug.Print ch.SeriesCollection(1).Formula
' =SERIES("Sales",Data!$A$2:$A$13,Data!$B$2:$B$13,1)
Esa fórmula SERIES(...) es toda la verdad sobre el gráfico: un nombre, una referencia de valores X, una
referencia de valores Y. El gráfico es una vista viva sobre esas referencias. Por eso «mi gráfico muestra
los datos equivocados» es tan común — el gráfico no está mal, su referencia está apuntando a celdas que ya
no contienen lo que crees. Y por eso la solución duradera, al final de esta guía, es hacer que la referencia
siga a los datos en lugar de congelarse en $B$2:$B$13.
Los dos contenedores: ChartObjects frente a hojas de gráfico
Excel tiene dos hogares para los gráficos, y confundirlos es lo primero que hace tropezar a una macro de
gráficos. Un gráfico incrustado flota sobre una hoja y vive en la colección ChartObjects de esa hoja;
una hoja de gráfico es una pestaña entera que no es más que un gráfico, y vive en la colección Charts
del libro:
' Incrustado: un ChartObject es el marco/contenedor; su .Chart es el grafico en si
Dim co As ChartObject
Set co = Worksheets("Report").ChartObjects.Add(300, 20, 420, 260)
co.Chart.ChartType = xlLine ' fija propiedades en .Chart, no en co
' Hoja de grafico: una pestana propia
Dim ch As Chart
Set ch = ThisWorkbook.Charts.Add
La sutileza que le cuesta una hora a la gente: con un gráfico incrustado, ChartObjects.Add te da el
marco (un ChartObject), y el gráfico que de verdad das formato es co.Chart. Echar mano de
Charts(1) cuando tu gráfico está incrustado lanza un error, porque Charts solo contiene hojas de
gráfico. La mayoría de los informes viven en una hoja de cálculo, así que usarás ChartObjects mucho más a
menudo — solo recuerda la división en dos capas marco/gráfico.
Crear un gráfico y enlazar los datos de una sola vez
El patrón limpio es: añade el marco, coge .Chart, fija el tipo, y enlaza todo el origen con
SetSourceData en una sola llamada:
Dim co As ChartObject, ch As Chart
Set co = Worksheets("Report").ChartObjects.Add(300, 20, 420, 260)
Set ch = co.Chart
ch.ChartType = xlColumnClustered
ch.SetSourceData Source:=Worksheets("Data").Range("A1:B13")
SetSourceData le entrega a Excel el bloque entero — encabezados, categorías y valores — y lo deja calcular
las series, exactamente igual que seleccionar el rango y pulsar el botón de gráfico. Esto es casi siempre lo
que quieres; son menos líneas y menos errores que construir las series de una en una. Incluye la fila de
encabezado y la columna de categorías en el rango y el gráfico se etiqueta solo. Fija .ChartType antes o
después — valores comunes son xlColumnClustered, xlLine, xlPie y xlXYScatter.
Añadir y controlar series a mano
Cuando necesitas control preciso — una serie a la vez, valores de un sitio no contiguo, un nombre
personalizado — añade series a través de SeriesCollection:
Dim s As Series
Set s = ch.SeriesCollection.NewSeries
s.Name = "2026"
s.XValues = Worksheets("Data").Range("A2:A13")
s.Values = Worksheets("Data").Range("B2:B13")
Cada serie tiene tres partes que se corresponden directamente con esa fórmula SERIES(...): .Name,
.XValues (las etiquetas de categoría) y .Values (los números). Añade una segunda serie con otro
NewSeries; quita una con ch.SeriesCollection(2).Delete. Fijar .Values a un rango es lo mismo que
escribir la referencia dentro de la fórmula de la serie — que es el mecanismo detrás del bug de los datos
equivocados, y la razón de que importe enlazar a un origen estable.
La trampa del gráfico duplicado, y limpiar los gráficos viejos
Aquí está el bug que produce doce gráficos tras doce ejecuciones: ChartObjects.Add siempre añade uno
nuevo. Nunca reutiliza el gráfico de la última vez, así que una macro sin vigilancia apila copias en
silencio. Limpia los gráficos existentes de la hoja antes de construir:
Dim co As ChartObject
For Each co In Worksheets("Report").ChartObjects
co.Delete ' quita antes los graficos de la ejecucion anterior
Next co
' ...ahora construye el grafico nuevo
Recorre ChartObjects y haz Delete a cada uno, y luego crea tu gráfico desde cero — la misma disciplina
de colección que usarías para cualquier construcción repetida. Si prefieres
conservar y actualizar un gráfico concreto, ponle nombre (co.Name = "chtSales"), búscalo por ese nombre en
la siguiente ejecución, y vuelve a llamar a SetSourceData en lugar de añadir un marco nuevo. Sea como sea,
decide a conciencia: construir de cero o actualizar en el sitio, nunca añadir a ciegas.
Enlazar el origen a una Table para que el gráfico siga a los datos
Ahora la solución duradera para el bug de los datos equivocados. Como una serie es una referencia, un
gráfico enlazado a A1:B13 se congela en trece filas — añade un mes y el punto nuevo queda fuera del
gráfico. Enlaza el origen a una Table en su lugar, y la referencia crece con los datos:
' El origen del grafico es el rango de una Table -> las filas nuevas aparecen solas
ch.SetSourceData Source:=Worksheets("Data").ListObjects("tblSales").Range
Cuando el origen es un ListObject, añadir una fila extiende la Table, la Table extiende el rango que
referencia la serie, y el gráfico se redibuja con el punto nuevo — sin código, sin volver a llamar a
SetSourceData. Este es el mismo movimiento que mantiene al día una
tabla dinámica, y es por lo que una Table es el cimiento
que vale la pena construir primero: apunta tus tablas dinámicas y gráficos a ella una vez y ambos se
autoactualizan. Para entregar un gráfico como imagen, co.Chart.Export "C:\Reports\sales.png" escribe un
PNG directamente en disco.
Cómo ayuda ExcelMaster
Los errores de gráficos que llegan a un informe terminado son silenciosos: el segundo gráfico que nadie
borró, la serie que aún apunta al rango del trimestre pasado, las barras que dibujan la columna equivocada
porque SetSourceData recibió un bloque que se había desplazado. Cada uno se dibuja limpiamente — solo que
muestra la imagen equivocada.
ExcelMaster construye gráficos como
alguien al que los tres le han quemado ya. Pídele «haz un gráfico de las ventas mensuales por región», y
limpia los ChartObjects viejos antes de dibujar, enlaza el origen a una Table para que las filas nuevas
aparezcan solas, usa SetSourceData para el bloque entero en lugar de cablear las series a mano, y da
formato a través de co.Chart en lugar de tropezar con la división marco/gráfico. Tú describes la imagen
que quieres; él conecta la referencia para que el gráfico siga diciendo la verdad a medida que los datos
crecen.
Preguntas frecuentes
¿Cómo creo un gráfico en VBA?
Añade un gráfico incrustado con ChartObjects.Add, y luego fija su tipo y sus datos a través de .Chart:
Set co = ws.ChartObjects.Add(300, 20, 420, 260), Set ch = co.Chart, ch.ChartType = xlColumnClustered,
ch.SetSourceData Source:=Range("A1:B13"). El ChartObject es el marco y su .Chart es el gráfico al que
das formato. SetSourceData enlaza todo el rango en una sola llamada.
¿Por qué mi gráfico de VBA dibuja los datos equivocados?
Porque un gráfico guarda una referencia a un rango, no una copia de él — el .Values de cada serie es una
fórmula del tipo =Sheet1!$B$2:$B$13. Si los datos se mueven, se insertan filas, o el origen se reconstruye
en otro sitio, la referencia sigue apuntando a las celdas viejas y el gráfico las dibuja. Vuelve a enlazar
con SetSourceData, o apunta el origen a una Table para que la referencia siga a los datos.
¿Cuál es la diferencia entre ChartObjects y Charts en VBA?
ChartObjects es la colección de gráficos incrustados de una hoja que flotan sobre ella; cada
ChartObject es un marco cuyo .Chart es el gráfico de verdad. Charts es la colección de hojas de
gráfico del libro — pestañas que no son más que un gráfico. Echar mano de Charts(1) cuando tu gráfico
está incrustado lanza un error; usa ws.ChartObjects(1).Chart en su lugar.
¿Cómo añado una serie a un gráfico en VBA?
Usa SeriesCollection.NewSeries, y luego fija sus partes: Set s = ch.SeriesCollection.NewSeries,
s.Name = "2026", s.XValues = Range("A2:A13"), s.Values = Range("B2:B13"). Añade más con otro
NewSeries; quita una con ch.SeriesCollection(2).Delete. Para un gráfico sencillo, SetSourceData sobre
todo el rango es más fácil que añadir series a mano.
¿Cómo evito que VBA cree gráficos duplicados?
ChartObjects.Add siempre añade un gráfico nuevo, así que reejecutar una macro apila copias. Antes de
construir, borra los existentes: For Each co In ws.ChartObjects: co.Delete: Next co. Si quieres conservar y
refrescar un gráfico concreto, ponle nombre (co.Name = "chtSales"), búscalo por ese nombre en la siguiente
ejecución, y vuelve a llamar a SetSourceData en lugar de añadir un marco nuevo.
Probado en
Probado en: Excel 365 (Windows 11), VBA 7.1 — última verificación el 02/09/2026.
Guías relacionadas: VBA Pivot Table · VBA Table · VBA Range · VBA Collection · VBA Worksheet
