Silverlight 4 — привязка не работает с перечисляемыми типами объектов POCO?

#c# #silverlight #xaml #data-binding #silverlight-4.0

#c# #silverlight #xaml #привязка к данным #silverlight-4.0

Вопрос:

У меня есть объект WCF RIA POCO -> Светофор, который имеет тип перечисления, описывающий его состояние.

 [DataContract]
public class TrafficLight
{
    [Key]
    [DataMember]
    public long Id { get; set; }    

    [DataMember]
    public Longitude Longitude { get; set; }

    [DataMember]
    public Latitude Latitude { get; set; }

    [DataMember]
    public TrafficLightState SelectedLight { get; set; }
}

[DataMember]
public enum TrafficLightState
{
    [EnumMember]
    Red = 0,

    [EnumMember]
    Yellow = 1,

    [EnumMember]
    Green = 2
}
  

Итак, учитывая службу RIA WCF, мне нужно было передать этот объект светофора в клиентское приложение Silverlight:

 public IEnumerable<TrafficLight> GetTrafficLightsForCity(int cityId)
{
    return Data.GetTrafficLightsForCity(cityId).AsEnumerable();
}
  

Так что все работает нормально. Я получаю свой список, и все хорошо, пока я не решу привязать список к сетке, в которой будут отображаться мои светофоры … (PS. Это игра вроде sim city):

Способ отображения светофоров в сетке — это небольшой элемент управления, который отображает состояние света -> красный, желтый, зеленый, плюс некоторые другие вещи. Сейчас я просто покажу вам статус и идентификатор Light:

 <UserControl x:Name="MiniTrafficLightControl" x:Class="UI.MiniTrafficLight"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="15" 
    d:DesignWidth="15"
    Width="15" Height="15">
    <StackPanel Orientation="Horizontal">
        <Border x:Name="BrdGreen" Visibility="Visible" CornerRadius="10" Width="15" Height="15" Margin="0" ToolTipService.ToolTip="Runing" d:IsLocked="True">
        <Border.Background>
            <RadialGradientBrush  GradientOrigin="0.4,0.3" Center="0.4,0.3" RadiusX="0.5" RadiusY="0.5">
            <RadialGradientBrush.GradientStops>
                <GradientStop Color="LightGreen" Offset="0" />
            <GradientStop Color="Green" Offset="0.95" />
            </RadialGradientBrush.GradientStops>
        </RadialGradientBrush>
        </Border.Background>
    </Border>
    <Border x:Name="BrdYellow" Visibility="Collapsed" CornerRadius="10" Width="15" Height="15" Margin="0" ToolTipService.ToolTip="Action Pending" d:IsHidden="True" d:IsLocked="True">
        <Border.Background>
            <RadialGradientBrush  GradientOrigin="0.4,0.3" Center="0.4,0.3" RadiusX="0.5" RadiusY="0.5">
            <RadialGradientBrush.GradientStops>
                <GradientStop Color="Yellow" Offset="0" />
            <GradientStop Color="Orange" Offset="0.95" />
            </RadialGradientBrush.GradientStops>
        </RadialGradientBrush>
        </Border.Background>
    </Border>
    <Border x:Name="BrdRed" Visibility="Collapsed" CornerRadius="10" Width="15" Height="15" Margin="0" ToolTipService.ToolTip="Stopped" d:IsHidden="True" d:IsLocked="True">
        <Border.Background>
            <RadialGradientBrush  GradientOrigin="0.4,0.3" Center="0.4,0.3" RadiusX="0.5" RadiusY="0.5">
            <RadialGradientBrush.GradientStops>
                <GradientStop Color="Orange" Offset="0" />
            <GradientStop Color="Red" Offset="0.95" />
            </RadialGradientBrush.GradientStops>
        </RadialGradientBrush>
        </Border.Background>
    </Border>
    </StackPanel>
</UserControl>
  

И код, лежащий в основе элемента управления:

 namespace UI
{
    public partial class MiniTrafficLight : UserControl
    {
        public static readonly DependencyProperty TrafficLightStateProperty = DependencyProperty.Register("TrafficLightState", typeof(TrafficLightState), typeof(MiniTrafficLight), null);

        public TrafficLightState TrafficLightState
    {
        get
        {
            return (TrafficLightState)this.GetValue(TrafficLightStateProperty);
        }
        set
        {
            if (value == TrafficLightState.Red)
        {
            this.BrdRed.Visibility = System.Windows.Visibility.Visible;
            this.BrdGreen.Visibility = System.Windows.Visibility.Collapsed;
            this.BrdYellow.Visibility = System.Windows.Visibility.Collapsed;
        }
        else if (value == TrafficLightState.Yellow)
        {
            this.BrdRed.Visibility = System.Windows.Visibility.Collapsed;
            this.BrdGreen.Visibility = System.Windows.Visibility.Collapsed;
            this.BrdYellow.Visibility = System.Windows.Visibility.Visible;
        }
        else
        {
             this.BrdRed.Visibility = System.Windows.Visibility.Collapsed;
                     this.BrdGreen.Visibility = System.Windows.Visibility.Visible;
             this.BrdYellow.Visibility = System.Windows.Visibility.Collapsed;
        }

                this.SetValue(TrafficLightStateProperty, value);
        }
        }
    }
}
  

итак, теперь проблема в том, что если где-нибудь на странице у меня есть сетка, которая отображает все объекты светофора с использованием элемента управления minitrafficlight внутри сетки — элемент управления MiniTrafficLight никогда не работает … или он никогда не отображает зеленые, красные или желтые огни. Я отладил службу и проверил, что свойство Traffic Lightstate устанавливается при его чтении из базы данных для каждого объекта traffic light. Я также могу убедиться, что в клиентском приложении silverlight состояние трафика для каждого объекта Traffic Light поступает правильно (иначе … оно установлено).

Однако, если я установлю контрольное значение Traffic Lightstate для MiniTrafficLight вручную без привязки, это сработает. Если я использую привязку, она никогда не появляется. С преобразователем или без него это свойство элемента управления MiniTrafficLight никогда не вызывается. Причина, по которой я знаю, заключается в том, что я добавил a MessageBox.Show(...) в набор средств доступа к свойству, и я не вижу никаких окон сообщений. Если я поставлю точки останова — они никогда не будут достигнуты. Ниже приведен пример кода, который отображает объекты светофора внутри сетки:

 <datagrid:DataGrid x:Name="ReceiverNodesDataGrid" Grid.Row="1" AutoGenerateColumns="False" IsReadOnly="True" HorizontalAlignment="Stretch" SelectionMode="Single">
    <datagrid:DataGrid.Columns>
        <datagrid:DataGridTemplateColumn Header="Status">
            <datagrid:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <local:MiniTraficLight TrafficLightState="{Binding TrafficLightState}"/>
        </DataTemplate>
            </datagrid:DataGridTemplateColumn.CellTemplate>
    </datagrid:DataGridTemplateColumn>
        <datagrid:DataGridTextColumn Header="Id" Binding="{Binding Id}" />
    </datagrid:DataGrid.Columns>
</datagrid:DataGrid>
  

Есть идеи, почему эта привязка не работает?

Спасибо всем, Мартин

Ответ №1:

Так что, по-видимому, хитрость в этом заключается в правильной реализации свойств зависимостей. ТЬФУ!

 namespace UI
{
    public partial class MiniTrafficLight : UserControl
    {
        private static PropertyMetaData trafficLightStateMetaData = new PropertyMetaData(new PropertyChangedCallBack(TrafficLightState_Changed));
        public static readonly DependencyProperty TrafficLightStateProperty = DependencyProperty.Register("TrafficLightState", typeof(TrafficLightState), typeof(MiniTrafficLight), trafficLightStateMetaData);

        public TrafficLightState TrafficLightState
        {
            get { return (TrafficLightState)this.GetValue(TrafficLightStateProperty); }
            set { this.SetValue(TrafficLightStateProperty, value); }
        }

        private static void TrafficLightState_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            MiniTrafficLight sender = d as MiniTrafficLight;
            TrafficLightState state = (TrafficLightState)e.NewState;

            if( state == TrafficLightState.Red )
            {
                //...
            }
            else if ( state == TrafficLightState.Yellow )
            {
                //...
            }
            else
            {
                //...
            }
        }
    }
}