Исключение типа NullReferenceExeption, где проблема?

205
17 декабря 2018, 17:00

Генерируется исключение типа

System.NullReferenceException: "Ссылка на объект не указывает на экземпляр объекта."

Вот такая у меня разметка XAML:

<Label x:Name="Label_Seconds" Content="0 секунд" HorizontalAlignment="Left" Margin="115,2,0,0" VerticalAlignment="Top" FontSize="16"/>
<Slider x:Name="Numbers_Slider" HorizontalAlignment="Left" Margin="10,33,0,0" VerticalAlignment="Top" Width="100" ValueChanged="Numbers_Slider_ValueChanged" Value="4" UseLayoutRounding="False" Minimum="4" Maximum="6"/>
<Label x:Name="Label_Numbers" Content="4 числа" HorizontalAlignment="Left" Margin="115,29,0,0" VerticalAlignment="Top" FontSize="16" RenderTransformOrigin="0.462,1.355"/>
<Slider x:Name="Seconds_Slider" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="100" ValueChanged="Seconds_Slider_ValueChanged" Value="4" UseLayoutRounding="False" Minimum="4" Maximum="6"/>

Вот код С#:

private void Numbers_Slider_ValueChanged( object sender, RoutedPropertyChangedEventArgs<double> e )
{
    // Здесь Label_Numbers == null
    Label_Numbers.Content = $"{Numbers_Slider.Value.ToInt32 ( )} числа";
}
private void Seconds_Slider_ValueChanged( object sender, RoutedPropertyChangedEventArgs<double> e )
{
    Label_Seconds.Content = $"{Seconds_Slider.Value} секунды";
}

А теперь к вопросу. По задумке, должен меняться текст Label в зависимости от значения слайдера. Но не тут то было. При первом запуске было все нормально, но потом нарисовал второй такой слайдер и тут же выбрасывает исключение. Где проблема и почему все таки Label_Numbers = null

Answer 1

Лучше всего в вашем случае подписываться на события после того как все элементы уже точно инициализированы:

public MainWindow()
{
    InitializeComponent();
    Numbers_Slider.ValueChanged += Numbers_Slider_ValueChanged;
    Seconds_Slider.ValueChanged += Seconds_Slider_ValueChanged;
}

А из разметки подписку убрать.

Дело в том, что интерпретатор XAML читает документ сверху вниз и при установке слайдеру свойства Value сразу срабатывает событие ValueChanged, но Label_Numbers здесь еще не существует, т.к. описан ниже, поэтому и возникает NRE.

Можно, конечно, поместить определение Label_Numbers в разметке выше Numbers_Slider, но это ненадежное решение.

А вообще, в WPF этот пример можно (и нужно) сделать вообще без подписки на события — с использованием механизма привязок. Также вам необходимо отказаться от "рисования контролов мышкой" и писать разметку вручную, активно используя панели компоновки (да, с ними надо ознакомиться, но их не много и знакомство с ними быстрое):

<Grid Margin="5">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="100"/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <Grid.Resources>
        <Style TargetType="TextBlock">
            <Setter Property="FontSize" Value="16"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="Margin" Value="5,2.5,0,2.5"/>
        </Style>
        <Style TargetType="Slider">
            <Setter Property="VerticalAlignment" Value="Center"/>
        </Style>
    </Grid.Resources>
    <Slider Name="Seconds_Slider" Value="4" Minimum="4" Maximum="6"/>
    <TextBlock Grid.Column="1" Text="{Binding ElementName=Seconds_Slider, Path=Value, StringFormat={}{0:0.##} секунд}"/>
    <Slider Grid.Row="1" Name="Numbers_Slider" Value="4" Minimum="4" Maximum="6"/>
    <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding ElementName=Numbers_Slider, Path=Value, StringFormat={}{0:0.##} числа}"/>
</Grid>

Да, разметка вышла более громоздкая за счет выноса одинакового форматирования в стиль, но когда контролов станет гораздо больше, эта громоздкость нивелируется, также стили можно вынести в отдельный словарь ресурсов или в App.xaml. Вынос оформления в стиль разгружает основную разметку — в ней остается только то что существенно здесь и сейчас, оформление же получается тоже скомпонованным в одном месте.

READ ALSO
ASP.NET проблемы с кириллицей внутри тега script

ASP.NET проблемы с кириллицей внутри тега script

Создадим строку с русскими символами в любом View

218
Передача id объекта View в метод контроллера ASP.NET MVC

Передача id объекта View в метод контроллера ASP.NET MVC

Есть БД с перечнем кандидатов, с помощью EF создал DbContext -

221
Запуск Тестов на нескольких браузерах Selenium + NUnit

Запуск Тестов на нескольких браузерах Selenium + NUnit

учусь писать автотесты, стал вопрос как запустить параллельное выполнение тестов на нескольких браузерах(допустим на 4х), если использовать...

206