Estoy tratando de entender cómo vincular la visibilidad de los elementos secundarios al tamaño de su elemento principal. Mi estrategia fue construir un convertidor que tenga un valor de umbral int (que corresponderá con el ancho de un control principal), y en base a ese valor de umbral, decide si un elemento secundario debe ser visible o no:
public class IntToVisibilityConverter : IValueConverter
{
public IntToVisibilityConverter()
{
FalseVisibility = Visibility.Collapsed;
Negate = false;
VisibilityThreshold = 0;
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int iVal;
bool result = int.TryParse(value.ToString(), out iVal);
if (!result) return value;
bool isVisible;
if (iVal < VisibilityThreshold)
{
isVisible = false;
}
else
{
isVisible = true;
}
isVisible = Negate ? !isVisible : isVisible;
if (isVisible)
{
return Visibility.Visible;
}
else
{
return FalseVisibility;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Visibility? val = null;
if (value is Visibility) val = (Visibility)value;
if (!val.HasValue) return value;
bool result = val == Visibility.Visible;
result = Negate ? !result : result;
if (result)
{
return VisibilityThreshold;
}
else
{
return VisibilityThreshold - 1;
}
}
public bool Negate { get; set; }
public int VisibilityThreshold { get; set; }
public Visibility FalseVisibility { get; set; }
}
Para probar cómo funciona, construí una interfaz de usuario muy simple que se ve así:
<Window.Resources>
<ResourceDictionary>
<local:IntToVisibilityConverter x:Key="IntConverter" VisibilityThreshold="600" Negate="True" />
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="1" x:Name="button1">
<Ellipse Fill="Orange" Width="100" Height="100" Visibility="{Binding ElementName=button1, Path=ActualWidth, Converter={StaticResource IntConverter}}" />
</Button>
</Grid>
Esto funciona bien sin problemas: cuando estiro la ventana y el botón alcanza un cierto ancho, la elipse desaparece y reaparece cuando contrasta la ventana.
Sin embargo, me encuentro con un comportamiento extraño una vez que agrego otra columna a la cuadrícula como esta:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="1" x:Name="button1">
<Ellipse Fill="Orange" Width="100" Height="100" Visibility="{Binding ElementName=button1, Path=ActualWidth, Converter={StaticResource IntConverter}}" />
</Button>
<Button Grid.Column="2" x:Name="button2">
<Ellipse Fill="DarkOrange" Width="100" Height="100" Visibility="{Binding ElementName=button2, Path=ActualWidth, Converter={StaticResource IntConverter}}" />
</Button>
</Grid>
Ahora, cuando ejecuto esta aplicación, si abro la ventana de modo que el ancho de los botones pase el umbral, en lugar de desaparecer las elipses, comienzan a parpadear. Es como si con cada cambio de ancho simplemente cambiaran su visibilidad hacia adelante y hacia atrás en lugar de colapsar permanentemente.
¿Alguien puede explicar por qué está haciendo eso y cómo podría hacer que funcione sin sorpresas?
Gracias.
El error principal es que su convertidor está tratando de hacer los cálculos matemáticos int
aunque todas las métricas de dimensión en WPF lo son double
. Incluso en el ejemplo supuestamente "funcional", la elipse solo desaparecerá cuando el ancho de la ventana sea un número entero exacto. De lo contrario, el convertidor falla en el TryParse()
método y luego devuelve lo que es para la Visibility
propiedad un valor sin sentido completo (es decir, el double
que se le pasó originalmente).
Cambié su convertidor para que se vea así y ambos ejemplos ahora funcionan perfectamente para mí:
public class IntToVisibilityConverter : IValueConverter
{
public IntToVisibilityConverter()
{
FalseVisibility = Visibility.Collapsed;
Negate = false;
VisibilityThreshold = 0;
}
public object Convert(object valueObject, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double value = (double)valueObject;
bool isVisible;
if (value < VisibilityThreshold)
{
isVisible = false;
}
else
{
isVisible = true;
}
isVisible = Negate ? !isVisible : isVisible;
if (isVisible)
{
//System.Diagnostics.Debug.WriteLine("isVisible");
return Visibility.Visible;
}
else
{
//System.Diagnostics.Debug.WriteLine("NOT isVisible");
return FalseVisibility;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Visibility? val = null;
if (value is Visibility) val = (Visibility)value;
if (!val.HasValue) return value;
bool result = val == Visibility.Visible;
result = Negate ? !result : result;
if (result)
{
return VisibilityThreshold;
}
else
{
return VisibilityThreshold - 1;
}
}
public bool Negate { get; set; }
public double VisibilityThreshold { get; set; }
public Visibility FalseVisibility { get; set; }
}
Si lo desea, puede realizar una comprobación adicional del valueObject
parámetro y regresar Binding.DoNothing
si valueObject is double
falla. En mi humilde opinión, es innecesario, pero evitará que el convertidor arroje una excepción momentáneamente durante la inicialización en caso de que obtenga algo DependencyProperty.UnsetValue
como el valor por alguna razón.
Este artículo se recopila de Internet, indique la fuente cuando se vuelva a imprimir.
En caso de infracción, por favor [email protected] Eliminar
Déjame decir algunas palabras