Autor
|
Tema: ListView con salto de línea /multilinea (Leído 11,840 veces)
|
okik
Desconectado
Mensajes: 462
|
hola, me gustaría saber si se puede introducir ítems en un Listbox de modo que se vean verticales del modo normal sería 12345678 45664555 78999999 Pero yo quiero hacerlo así 1 4 7 2 5 8 3 6 9 4 6 9 5 4 9 6 5 9 7 5 9 8 5 9 Si hago esto por ejemplo... Dim Num() As String = {"1", "2", "3", "4"} Dim strLinea As String = Nothing strLinea = String.Join(Environment.NewLine, Num).Trim ListBox1.Items.Add(strLinea) Label1.Text= strLinea
el Label1 muestra correctamente: Mientras que un Listbox o un ListView lo mostraría así El ListBox aunque lo mostrara como el label pondría uno debajo del otro. Con un Listview podría ponerlo en cada columna, pero también lo pone horizontal. Lo he hecho con un FlowLayoutPanel1 Panel, y añado Labels de forma automática pero no es lo que busco. Además que me ocupa mucho código y es tedioso de hacer. ¿Alguna idea? Gracias
Acebo de darme cuenta que además con Panel, no puedo hacer multiselección
|
|
« Última modificación: 11 Diciembre 2016, 16:15 pm por okik »
|
En línea
|
|
|
|
Eleкtro
Ex-Staff
Desconectado
Mensajes: 9.866
|
Hola. El problema con un ListBox y un ListView es que automaticamente acomodan los caracteres de salto de linea para la vista en horizontal, para representarlo como una sola linea. Me parece que la única solución sería heredar la class ListBox o ListView con OwnerDraw para dibujar manualmente los rectángulos de los items y el contenido de texto, se puede hacer (al menos con un ListView), pero es una solución tediosa que requeriría tiempo y esfuerzo.
Con un ListBox, para hacerlo vertical puedes pasarle un array, esto significa que si tenemos el string "12345678", cada caracter "{1, 2, 3, 4, 5, 6, 7, 8}" será seleccionado por individual, no se si eso te parecerá bien. With ListBox1 .MultiColumn = True .IntegralHeight = False .Font = New Font(.Font.FontFamily, 14.0F) .ColumnWidth = CInt(Math.Ceiling(.Font.Size)) .Size = New Size((.ColumnWidth * 4), 200) End With Dim arr1 As String() = {"1", "2", "3", "4", "5", "6", "7", "8"} Dim arr2 As String() = {"4", "5", "6", "6", "4", "5", "5", "5"} Dim arr3 As String() = {"7", "8", "9", "9", "9", "9", "9", "9"} ListBox1.Items.AddRange(arr1) ListBox1.Items.AddRange(arr2) ListBox1.Items.AddRange(arr3)
Con un ListView, puedes utilizar el modo de vista LargeIcon, sin embargo, para que se muestre en vertical debemos activar la propiedad LabelWrap, y esto nos da un resultado visual poco agradable sobre los items que no están seleccionados, puesto que no podemos redimensionar el tamaño de los rectángulos de cada item a menos que heredemos el control: With ListView1 .Font = New Font(.Font.FontFamily, 12.25F) .View = View.LargeIcon .LabelWrap = True End With Dim str1 As String = String.Join(ControlChars.Lf, {"1", "2", "3", "4", "5", "6", "7", "8"}) Dim str2 As String = String.Join(ControlChars.Lf, {"4", "5", "6", "6", "4", "5", "5", "5"}) Dim str3 As String = String.Join(ControlChars.Lf, {"7", "8", "9", "9", "9", "9", "9", "9"}) Dim item1 As New ListViewItem(str1) Dim item2 As New ListViewItem(str2) Dim item3 As New ListViewItem(str3) ListView1.Items.AddRange({item1, item2, item3})
Con un DataGridView ocurre exactamente lo mismo por defecto, los caracteres de salto linea se acomodan para la vista en horizontal (las lineas en blanco no se eliminan del item, tampoco en un Listview, simplemente el control representa el texto en las filas sin las lineas en blanco), sin embargo, el DataGridView es un control mucho más personalizable que un ListView, así que podemos adaptarlo a nuestras necesidades de vista de filas en vertical y el resultado quedará bastante bien: With DataGridView1 .Columns.Add("Column1", "") .Columns.Add("Column2", "") .Columns.Add("Column3", "") .Font = New Font(.Font.FontFamily, 12.25F) .AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells .DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter .DefaultCellStyle.WrapMode = DataGridViewTriState.True End With Dim str1 As String = String.Join(ControlChars.Lf, {"1", "2", "3", "4", "5", "6", "7", "8"}) Dim str2 As String = String.Join(ControlChars.Lf, {"4", "5", "6", "6", "4", "5", "5", "5"}) Dim str3 As String = String.Join(ControlChars.Lf, {"7", "8", "9", "9", "9", "9", "9", "9"}) DataGridView1.Rows.Add({str1, str2, str3})
En resumen, tu mejor opción es recurrir al control DataGridView, o bien heredar la class ListView, activar el OwnerDraw para dibujar manualmente el contenido del control y trastear con el StringFormat, StringAlignment, y los Bounds para intentar conseguir el resultado de vista en vertical (que no estoy muy seguro de si se podrá, depende de los miembros que sean accesibles]). Saludos!
|
|
« Última modificación: 11 Diciembre 2016, 19:56 pm por Eleкtro »
|
En línea
|
|
|
|
okik
Desconectado
Mensajes: 462
|
Gracias @Elektro Hay que ver como te curras las respuestas, me sabe mal y todo. el DataGridView parece una buena opción, lo probaré a ver. No se me había ocurrido probar con él. La verdad es que no entiendo como no se puede. Como aquí en la primera columna: Lo intenté con API, con SendMessage, y probando conseguí cosas interesantes pero no eso.
|
|
|
En línea
|
|
|
|
Eleкtro
Ex-Staff
Desconectado
Mensajes: 9.866
|
La verdad es que no entiendo como no se puede. Como aquí en la primera columna: Lo intenté con API, con SendMessage, y probando conseguí cosas interesantes pero no eso. Poder se puede, como ya dije, pero es que lo que muestras en esa imagen, no es un ListView por defecto, en todo cado puede ser un DataGridView modificado, o un ListView que ha sido modificado, es decir, un user-control personalizado. Puedes hacer lo mismo que en esa imagen, como ya dije, heredando la class ListView. Aquí tienes un ejemplo base: No necesitas en ningún momento recurrir a las funciones de la API de Windows, eso déjalo para circunstancias en donde no puedas controlarlo de otra forma más directa; al heredar la class ListView, obtienes todo el control necesario, haciendo uso de los miembros heredados, para personalizar la manera en que se renderiza el control (añadir barras de progreso, hacer el texto multi-linea, cambiar los colores por defecto, etc), simplemente aprende a hacerlo: Pero ya te digo, lo veo una pérdida de tiempo, cuando puedes usar un DataGridView y darle una apariencia similar a un ListView (suponiendo que eso sea lo que te frena). Saludos!
|
|
« Última modificación: 12 Diciembre 2016, 03:24 am por Eleкtro »
|
En línea
|
|
|
|
ivancea96
Desconectado
Mensajes: 3.412
ASMático
|
Esos saltos de línea que ves en la primera columna los genera automáticamente el DataGridView cuando le colocas a la columna DefaultCellStyle->WrapMode = true Solo existen en la visualización; si agrandases la columna, se recolocaría.
|
|
|
En línea
|
|
|
|
okik
Desconectado
Mensajes: 462
|
Gracias El DataGridView va perfecto. No se me ocurrió utilizarlo porque lo usaba para base de datos, modificar tablas . Public Class Form1 Dim DataGridView1 As New DataGridView Sub New() ' Llamada necesaria para el diseñador. InitializeComponent() ' Agregue cualquier inicialización después de la llamada a InitializeComponent(). Me.Controls.Add(Me.DataGridView1) End Sub Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load With DataGridView1 .DefaultCellStyle.WrapMode = DataGridViewTriState.True .RowHeadersVisible = False .ColumnHeadersVisible = False .ReadOnly = True .RowCount = 1 .AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCells ' .AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells .MultiSelect = True .ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableWithoutHeaderText 'Permite copiar el texto .RowsDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter 'coloca los valores en el centro .Size = New Size(New Point(400, 235)) End With 'Configuración de las cabeceras de las columna With DataGridView1.ColumnHeadersDefaultCellStyle .Font = New Font("Lucida Console Unicode", FontStyle.Bold) .Alignment = DataGridViewContentAlignment.MiddleCenter 'coloca el texto en el centro End With Dim nvars As Integer = 20 DataGridView1.ColumnCount = nvars Task.Factory.StartNew(Sub() For I As Integer = 0 To nvars - 1 Dim Integ As Integer = I Me.Invoke(DirectCast(Sub() DataGridView1.Columns(Integ).Width = 30 DataGridView1.Item(Integ, 0).Value = Serie1() System.Threading.Thread.Sleep(50) End Sub, MethodInvoker)) Next End Sub) End Sub Public Function Serie1() As String Dim col(16) As String Dim Rand As New Random For index As Integer = 1 To col.Count - 1 col(index) = CStr(Rand.Next(0, 16)) Next Return CStr(String.Join(Environment.NewLine, col).Trim) End Function End Class
Lo malo es que no parece que pueda evitar que las columnas no sean redimensionables y al mismo tiempo en la creación de la columna establecer el ancho de la misma.
|
|
« Última modificación: 12 Diciembre 2016, 23:13 pm por okik »
|
En línea
|
|
|
|
ivancea96
Desconectado
Mensajes: 3.412
ASMático
|
Sí, puedes evitar que sean redimensionables, una a una. Tienes que ir a editar columnas y ahí ves todas sus propiedades.
También tiene el DataGridView unos campos que son los "default", que afectan a todas las columnas, filas y celdas que no tengan un "valor explícito".
Todo lo puedes hacer, échale un ojo a las propiedades detenidamente.
|
|
|
En línea
|
|
|
|
okik
Desconectado
Mensajes: 462
|
Sí, puedes evitar que sean redimensionables, una a una. Tienes que ir a editar columnas y ahí ves todas sus propiedades.
También tiene el DataGridView unos campos que son los "default", que afectan a todas las columnas, filas y celdas que no tengan un "valor explícito".
Todo lo puedes hacer, échale un ojo a las propiedades detenidamente.
claro que puedo evitar que sea redimensionable si ajusto establezco: DataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells Pero entonces ya no puedo establecer el ancho, aunque lo haga lo ignora.
Vale, la solución es usar Padding junto con DisplayedCells DataGridView1.DefaultCellStyle.Padding = New Padding(5, 2, 5, 2) DataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells
Donde: Padding(X(izquierdo), Y(Arriba), X(derecho), Y(abajo)) Public Class Form1 Dim DataGridView1 As New DataGridView Sub New() ' Llamada necesaria para el diseñador. InitializeComponent() ' Agregue cualquier inicialización después de la llamada a InitializeComponent(). Me.Controls.Add(Me.DataGridView1) End Sub Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load With DataGridView1 .DefaultCellStyle.WrapMode = DataGridViewTriState.True .RowHeadersVisible = False .ColumnHeadersVisible = False .ReadOnly = True .RowCount = 1 .AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCells .DefaultCellStyle.Padding = New Padding(5, 2, 5, 2) .AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells .MultiSelect = True .ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableWithoutHeaderText 'Permite copiar el texto .RowsDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter 'coloca los valores en el centro .Size = New Size(New Point(400, 240)) End With Dim nvars As Integer = 20 DataGridView1.ColumnCount = nvars Task.Factory.StartNew(Sub() For I As Integer = 0 To nvars - 1 Dim Integ As Integer = I Me.Invoke(DirectCast(Sub() DataGridView1.Item(Integ, 0).Value = Serie1() System.Threading.Thread.Sleep(50) End Sub, MethodInvoker)) Next End Sub) End Sub Public Function Serie1() As String Dim col(16) As String Dim Rand As New Random For index As Integer = 1 To col.Count - 1 col(index) = CStr(Rand.Next(0, 16).ToString("00")) Next Return CStr(String.Join(Environment.NewLine, col).TrimStart) End Function End Class
Bueno, pues GRACIAS a los dos. Lo doy por solucionado
|
|
« Última modificación: 12 Diciembre 2016, 23:44 pm por okik »
|
En línea
|
|
|
|
ivancea96
Desconectado
Mensajes: 3.412
ASMático
|
Para evitar usar padding y tener un ancho fijo, le colocas el Width, le colocas Resizable a False y AutoSize a None.
|
|
|
En línea
|
|
|
|
Eleкtro
Ex-Staff
Desconectado
Mensajes: 9.866
|
Conviene que no interactues con el control para construir las columnas y filas, en su lugar puedes definir un DataTable y construir la tabla allí, y entonces usar la propiedad DataSource del DataGridView. Recuerda, cuanta menos interacción directa exista por parte de tu código con los controles, mejor. He extendido mucho (realmente mucho) el siguiente ejemplo, pero bueno, así es como lo haría yo: Primeramente, definimos un type para almacenar y/o generar secuencias numéricas aleatorias. Aparte de servirnos para la representación epxlícita de este tipo de secuencias numéricas, otra diferencia (o ventaja) entre una colección List(Of Integer()) y esto, es que podremos reutilizarlo para muchos otros propósitos en el futuro: Public Class RandomSequence Protected Shared ReadOnly rand As New Random() Public ReadOnly Property Value As Integer() Get Return Me.valueB End Get End Property Protected valueB As Integer() Public ReadOnly Property Value(ByVal format As String) As String Get Return Me.ToString(New IntegerSequenceFormatter(), format) End Get End Property Public ReadOnly Property Length As Integer Get Return Me.lengthB End Get End Property Protected lengthB As Integer Private Sub New() End Sub Protected Friend Sub New(ByVal value As Integer()) Me.valueB = value Me.lengthB = value.Length End Sub Protected Friend Sub New(ByVal length As Integer) Me.valueB = Me.GenerateSequence(length).ToArray() Me.lengthB = length End Sub Protected Friend Sub New(ByVal length As Integer, ByVal minValue As Integer, ByVal maxValue As Integer) Me.valueB = Me.GenerateSequenceInternal(length, minValue, maxValue).ToArray() Me.lengthB = length End Sub Public Overridable Function GenerateSequence(ByVal length As Integer) As Integer() Me.valueB = Me.GenerateSequenceInternal(length, minValue:=0, maxValue:=Integer.MaxValue).ToArray() Return Me.valueB End Function Public Overridable Function GenerateSequence(ByVal length As Integer, ByVal minValue As Integer, ByVal maxValue As Integer) As Integer() Me.valueB = Me.GenerateSequenceInternal(length, minValue, maxValue).ToArray() Return Me.valueB End Function Protected Iterator Function GenerateSequenceInternal(ByVal length As Integer, ByVal minValue As Integer, ByVal maxValue As Integer) As IEnumerable(Of Integer) For i As Integer = 0 To (length - 1) Yield rand.Next(minValue, maxValue) Next i End Function Public Overrides Function Equals(ByVal obj As Object) As Boolean If (TypeOf obj IsNot RandomSequence) Then Return False Else Return String.Join(" ", Me.valueB).Equals(String.Join(" ", DirectCast(obj, RandomSequence).valueB)) End If End Function Public Overridable Shadows Function ToString(ByVal formatProvider As IFormatProvider, ByVal format As String) As String If (TypeOf formatProvider Is ICustomFormatter) Then Return DirectCast(formatProvider, ICustomFormatter).Format(format, Me.valueB, formatProvider) ElseIf (formatProvider IsNot Nothing) Then Return formatProvider.ToString() Else Return Me.valueB.ToString() End If End Function <EditorBrowsable(EditorBrowsableState.Never)> Public Overridable Shadows Function GetHashCode() As Integer Return MyBase.GetHashCode() End Function <EditorBrowsable(EditorBrowsableState.Never)> Public Overridable Shadows Function [GetType]() As Type Return MyBase.GetType() End Function <EditorBrowsable(EditorBrowsableState.Never)> Public Shared Shadows Function ReferenceEquals(ByVal objA As Object, ByVal objB As Object) As Boolean Return Object.ReferenceEquals(objA, objB) End Function End Class
Ejemplo de uso: Dim seq1 As New RandomSequence(length:=3) ' minValue:=0, maxValue:=Integer.MaxValue Dim seq2 As New RandomSequence(length:=3, minValue:=0, maxValue:=100) Dim seq3 As New RandomSequence({1, 2, 3}) Console.WriteLine(seq1.Length) Console.WriteLine(seq1.Equals(seq2)) Console.WriteLine(seq1.Value.ToString()) Console.WriteLine(seq1.Value("Horizontal")) ' o "H" Console.WriteLine(seq1.Value("Vertical")) ' o "V"
Seguidamente, definimos un segunto Type donde implementaremos las interfaces IFormatProvider y ICustomFormatter, y así desarrollaremos el algoritmo de formateo para representar en texto horizontal o vertical una secuencia numérica. La gran ventaja de desarrollar nuestro propio proveedor de formato es que se puede adaptar para utilizarlo de mil formas distintas, en cientos de situaciones diferentes. Public Class IntegerSequenceFormatter : Implements IFormatProvider, ICustomFormatter Public Function GetFormat(formatType As Type) As Object Implements IFormatProvider.GetFormat If formatType Is GetType(ICustomFormatter) Then Return Me Else Return Nothing End If End Function Public Function Format(fmt As String, arg As Object, formatProvider As IFormatProvider) As String Implements ICustomFormatter.Format If Not Me.Equals(formatProvider) Then Return Nothing End If If String.IsNullOrEmpty(fmt) Then Dim result As Integer() = TryCast(arg, Integer()) If (result Is Nothing) Then Throw New ArgumentException("Value is not of type Integer()", paramName:="arg") Exit Function End If End If If String.IsNullOrEmpty(fmt) Then Throw New ArgumentNullException(paramName:="fmt") Exit Function End If Select Case fmt.ToLower() Case "h", "horizontal" Dim seq As Integer() = DirectCast(arg, Integer()) Dim maxLength As Integer = CStr(seq.Max()).Length Dim sb As New StringBuilder(capacity:=seq.Length * maxLength) For Each item As Integer In seq sb.Append(item.ToString().PadLeft(maxLength, "0"c)) sb.Append(" "c) Next Return sb.ToString.TrimEnd(" "c) Case "v", "vertical" Dim seq As Integer() = DirectCast(arg, Integer()) Dim maxLength As Integer = CStr(seq.Max()).Length Dim sb As New StringBuilder(capacity:=seq.Length * maxLength) For Each item As Integer In seq sb.AppendLine(item.ToString().PadLeft(maxLength, "0"c)) Next Return sb.ToString() Case Else Throw New FormatException(String.Format("'{0}' cannot be used to format {1}.", fmt, arg.ToString())) End Select End Function End Class
Ejemplo de uso: Dim arr As Integer() = {1, 2, 3, 4, 5} Dim strHorz As String = String.Format(New IntegerSequenceFormatter, "{0:H}", arr) Dim strVert As String = String.Format(New IntegerSequenceFormatter, "{0:V}", arr) Console.WriteLine(strHorz) Console.WriteLine(strVert)
Nota: En el el type RandomSequence no necesitaremos utilizarlo pasando tantos argumentos, lo acortaremos a RandomSequence.Value("H") y RandomSequence.Value("V").
Por último, solo nos queda hacer uso de todo esto para construir la tabla y representarla en el control DataGridView: Public Class Form1 : Inherits Form Dim seqList As New List(Of RandomSequence) Dim seqTable As New DataTable("RandomSequenceTable") Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load Dim seqCount As Integer = 20 ' Amount of random sequences to generate. ' Build random sequences list. This is optional, just to have a short reference to a generic collection in source-code. For i As Integer = 0 To (seqCount - 1) seqList.Add(New RandomSequence(length:=16, minValue:=0, maxValue:=16)) Next ' Build table from list. For x As Integer = 0 To (seqList.Count - 1) seqTable.Columns.Add(New DataColumn) seqTable.Columns(x).DataType = GetType(RandomSequence) Next seqTable.Rows.Add.ItemArray = seqList.ToArray() ' To build a data-table of vertical strings representation: ' seqTable.Rows.Add.ItemArray = (From seq As RandomSequence In seqList Select seq.Value("V")).ToArray() seqTable.AcceptChanges() ' Build grid. DataGridView1.SuspendLayout() With DataGridView1 .AllowUserToAddRows = False .AllowUserToResizeColumns = False .AutoGenerateColumns = True .AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells .AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCells .ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableWithoutHeaderText .ColumnHeadersVisible = False .DefaultCellStyle.Padding = New Padding(5, 2, 5, 2) .DefaultCellStyle.WrapMode = DataGridViewTriState.True .MultiSelect = True .ReadOnly = True .RowHeadersVisible = False .RowsDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter .Size = New Size(New Point(400, 240)) End With DataGridView1.DataSource = seqTable DataGridView1.ResumeLayout() End Sub Private Sub DataGridView1_CellFormatting(ByVal sender As Object, ByVal e As DataGridViewCellFormattingEventArgs) _ Handles DataGridView1.CellFormatting If (e.Value IsNot Nothing) Then Dim item As RandomSequence = TryCast(e.Value, RandomSequence) If (item IsNot Nothing) Then e.Value = item.Value("Vertical") End If End If End Sub End Class
Nótese que el contenido de las celdas se formatean controlando el evento DataGridView1.CellFormatting, esto es algo ilustrativo y opcional, se puede contruir la data-table con las celdas ya formateadas para no tener que hacerlo después controlando ese evento, pero veo de mayor utilidad tener un data-table de RandomSequence, que de Strings multilinea...
Resultado de ejecución: Saludos!
|
|
« Última modificación: 13 Diciembre 2016, 15:34 pm por Eleкtro »
|
En línea
|
|
|
|
|
Mensajes similares |
|
Asunto |
Iniciado por |
Respuestas |
Vistas |
Último mensaje |
|
|
salto de línea en ficheros
Programación Visual Basic
|
AGRIPI
|
1
|
2,206
|
25 Mayo 2006, 15:06 pm
por sch3m4
|
|
|
Salto de linea en Batch
Scripting
|
Jatt
|
2
|
13,414
|
5 Enero 2007, 18:55 pm
por Jatt
|
|
|
¿Borrar linea 1 de textbox multilinea y que la 2ª, suba a la primera?
.NET (C#, VB.NET, ASP)
|
usuario oculto
|
5
|
10,648
|
4 Agosto 2011, 19:48 pm
por usuario oculto
|
|
|
Salto de línea
Desarrollo Web
|
bgnumis
|
2
|
4,216
|
25 Junio 2018, 19:49 pm
por JUCA
|
|
|
Extraer determinada linea de un textbox multilinea
Programación Visual Basic
|
rapbyone
|
1
|
3,378
|
6 Diciembre 2019, 20:50 pm
por Serapis
|
|