2011-06-01 9 views
9

Cuando trato de enlazar un valueconverter de un Estado enumeración definido para cepillar, me sale un error en mi diseñador XAML:Uso de recursos como resultado convertion en un convertidor se unen

recursos 'OKStatus' not found.

La aplicación funciona bien en tiempo de ejecución, pero no puedo ver mi GUI en el diseñador. Mis recursos se definen en el archivo color.xaml, que se lee en tiempo de ejecución. Todo el código se encuentra dentro del mismo espacio de nombres

Mi XAML:

xmlns: config = "CLR-espacio de nombres: App.MyNamespace"

<UserControl.Resources> 
    <ResourceDictionary> 
     <ResourceDictionary.MergedDictionaries> 
      <ResourceDictionary Source="c:\Skins\Colors.xaml" /> 
      <ResourceDictionary Source="c:\Skins\Common.xaml" />     
     </ResourceDictionary.MergedDictionaries> 
     <config:StatusConverter x:Key="StateConverter" /> 
     <config:BoolConverter x:Key="BoolConverter" /> 
     <config:BooleanConverter x:Key="BooleanConverter" /> 
    </ResourceDictionary> 
</UserControl.Resources> 

y

Estado

Mi conver ter:

[ValueConversion(typeof(bool), typeof(Brush))] 
public class BoolConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, 
     object parameter, CultureInfo culture) 
    { 
     bool state = (bool)value; 

     FrameworkElement FrameElem = new FrameworkElement(); 

     if (state == true) 
      return (FrameElem.FindResource("OKStatus") as Brush); 
     else 
      return (FrameElem.FindResource("ErrorStatus") as Brush); 
    } 

    public object ConvertBack(object value, Type targetType, 
     object parameter, CultureInfo culture) 
    { 
     return null; 
    } 
} 

En este código, el frameElem no tendrá ningún conocimiento de los recursos que he definido supongo, así que necesito una manera de obtener acceso a mis recursos durante el diseño. ¿Esto es posible?

Respuesta

-1

En realidad lo que terminé haciendo (por ahora) fue cambiar de FindResource a TryFindResource, y poner las cuentas en un bloque try/catch. Esto parece funcionar hasta ahora.

try 
{ 
    if (state == true) 
     return (FrameElem.TryFindResource("OKStatus") as Brush); 
    else 
     return (FrameElem.TryFindResource("ErrorStatus") as Brush); 
} 

catch (ResourceReferenceKeyNotFoundException) 
{ 
    return new SolidColorBrush(Colors.LightGray); 
} 
+0

Personalmente fui con la solución de Joel anterior, ya que parece mucho menos computacionalmente intensiva. TryFindResource pasaría factura en mi aplicación donde tuve que modificar los colores de los elementos en una gran cuadrícula de datos. – Eternal21

6

Creo que el problema es que está tratando de encontrar el recurso fuera de un elemento de marco que no está en el árbol visual. ¿Podría probar lo siguiente en su lugar?

Application.Current.FindResource("OKStatus") as Brush; 
+0

que me haga saber si estoy fuera de la base. –

+1

Eso definitivamente funcionaría si los recursos estuvieran en el diccionario de nivel de aplicación o en los recursos del sistema. –

11

Sí, es posible, y su conjetura es correcta. La búsqueda de recursos comienza con el árbol lógico, y la creación de un nuevo FrameworkElement() no satisface esto. Está completamente desconectado.

Lo que puede hacer (y lo que es posible que tenga que hacer si la sugerencia del N8 no funciona), es entregar su convertidor una referencia a la UserControl como el FrameworkElement llamar FindResource() sucesivamente.

La razón por la sugerencia de N8 probablemente no funcione es que Application.Current.FindResource() probablemente comienza a recursos a nivel de aplicación y luego se va a los recursos del sistema, pero los recursos que está buscando se encuentran en los recursos del UserControl 's. Si se colocaran en los recursos de App.xaml, funcionaría. Sin embargo, creo que Application.Current puede ser null en el momento del diseño.

La forma más sencilla que puedo pensar para hacer esto es en el constructor de sus UserControl 's:

public MyUserControl(){ 
    var boolconv = new BoolConverter(); 
    boolconv.FrameworkElement = this; 
    this.Resources.Add("BoolConverter", boolconv); 
    InitializeComponent(); 
} 

Estoy bastante seguro de que va delante de InitializeComponent(), en lugar de después.

Hacer esto en XAML sería más complicado, ya que probablemente tenga que agregar un DependencyProperty a su convertidor para que pueda enlazar el UserControl a él. Creo que eso sería ir por la borda.

Otra forma es poner TrueBrush y FalseBrush propiedades en su convertidor y asignarlas en XAML, que es lo que suelo hacer para que mis conversores sean vagos y de uso general. (NB: los nombres son ligeramente diferentes.)

<config:BoolToBrushConverter x:Key="Bool2Brush" 
         TrueBrush="{StaticResource OKStatusBrush}" 
         FalseBrush="{StaticResource ErrorStatusBrush}" /> 
+1

Gracias Joel, su solución parece mucho más limpia. Soy bastante nuevo en el mundo de WPF. –

+0

Esto (usando propiedades) es definitivamente una mejor solución. – CJBrew

0

Me he encontrado con este problema también. Creo que llamar al Application.Current es la mejor manera de obtener recursos de un IValueConverter, ya que no están definidos en un nivel por ventana, página o control. Esto requeriría que los recursos sean al menos nivel de aplicación, como se indicó anteriormente.

Sin embargo, dado que la referencia Application.Current se establece como nula en el diseñador, este método siempre romperá el diseñador. Lo que parece que ha hecho le ha dado algo para que el diseñador lo muestre, mientras le ha dado acceso a la aplicación en ejecución a los recursos en el convertidor.

Para todos ustedes con este problema, no es necesario implementar el Kludge que lewi implementó; esto es solo si desea cargar la superficie del diseñador. No afecta su aplicación mientras se ejecuta, ya que la llamada Application.Current tiene algo que ver.

0

Como he aprendido en TechNet Wiki, es necesario usar el convertidor MultiValue y MultiValueBinding para obtener el convertidor registrado correcto y corregir FrameworkElement por UserControl.

XAML Ejemplo:

<TextBlock x:Name="tb1" Margin="20"> 
<TextBlock.Text> 
    <MultiBinding Converter="{StaticResource MyConverter}"> 
    <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type UserControl}}"/> 
    <Binding Path="MyValue"/> 
    </MultiBinding> 
</TextBlock.Text> 
</TextBlock> 

A continuación, la declaración del convertidor CAN miradas:

public class MyConverter : IMultiValueConverter 
{ 
    FrameworkElement myControl; 
    object theValue; 

    public object Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     myControl = values[0] as FrameworkElement; 
     theValue = values[1]; 

     return myControl.FindResource(">>resource u need<<"); 
     } 

     public object[] ConvertBack(object value, System.Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) 
    { 
    ..... 
    } 
} 

El detalle explicación es: https://social.technet.microsoft.com/wiki/contents/articles/12423.wpfhowto-pass-and-use-a-control-in-it-s-own-valueconverter-for-convertconvertback.aspx

Cuestiones relacionadas