Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > .NET для новичков > проблема


Автор: fromrus 20.6.2008, 14:52
Код

public static void DisplayFoldersAndFiles(string str, ListBox list)
        {
            DirectoryInfo[] dirs = new DirectoryInfo(str).GetDirectories();
            FileInfo[] files = new DirectoryInfo(str).GetFiles(); ;
            list.Items.Clear();
            //list1.Items.Add((object)"[...]");
            foreach (DirectoryInfo dir in dirs)
            {
                list.Items.Add(dir.Name);
            }
            foreach (FileInfo file in files)
            {
                list.Items.Add(file.Name);
            }

Код

void DisplayLocalDrives()
        {
            DriveInfo[] drives = DriveInfo.GetDrives();           
            foreach (DriveInfo drive in drives)
            {
                b = new Button(); b1 = new Button();
                b.Content = drive.Name; b.Click += LocalDiskClick;
                b1.Content = drive.Name; b1.Click += LocalDiskClick1;
                b.Margin = new Thickness(3); b1.Margin = new Thickness(3);
                leftPanel.Children.Add(b); rightPanel.Children.Add(b1);
            }
        }

Код

void LocalDiskClick(object sender, RoutedEventArgs e)
        {
            FileSystem.DisplayFoldersAndFiles(b.Content.ToString(),leftBox);
        }

Функция DisplayFoldersAndFiles-отображает файлы и папки в ЛистБоксе по данному(str) адресу.Функция DisplayLocalDrives-отображает кнопки,в которых написаны имена локальных дисков,по нажатию на кнопку в лист боксе отображается содержимое конкретного диска.
В обработчике просто вызывается DisplayFoldersAndFiles.Дело в том,что при дебаге я узнал(неожиданно для себя),что str-параметр всегда равен "K:\\",т.е. я кликаю на кнопку,где написано C:\\ (str="C:\\"),а у меня все равно str="K:\\"(это съемник в моей системе,стоит последним в списке дисков).В чем проблема?

Автор: Idsa 20.6.2008, 15:22
fromrus, для WPF у нас есть отдельный раздел. В будущем задавайте вопросы там.

Теперь по коду. Такое ощущение, что b у вас объявлена на уровне класса, поэтому неудивительно, что в этой переменной хранится ссылка на последнюю кнопку, создаваемую в foreach.
1. Необходимо убрать переменную b из объявления на уровне класса.
2. В методе DisplayLocalDrives для создания кнопки необходимо использовать локальную переменную:
Код

void DisplayLocalDrives()
        {
            DriveInfo[] drives = DriveInfo.GetDrives();           
            foreach (DriveInfo drive in drives)
            {
                Button b = new Button(); Button b1 = new Button(); //добавил объявление переменных b и b1
                b.Content = drive.Name; b.Click += LocalDiskClick;
                b1.Content = drive.Name; b1.Click += LocalDiskClick1;
                b.Margin = new Thickness(3); b1.Margin = new Thickness(3);
                leftPanel.Children.Add(b); rightPanel.Children.Add(b1);
            }
        }

3. В обработчике события приводим Sender к типу кнопки и  получаем имя диска:
Код

void LocalDiskClick(object sender, RoutedEventArgs e)
        {
            Button button = (Button)sender;
            FileSystem.DisplayFoldersAndFiles(button.Content.ToString(),leftBox);
        }

Автор: fromrus 20.6.2008, 17:26
ошибка была втом,что я не привел sender к типу Button,а с объявлением на уровне класса все норм-и так работает smile 
Я правда не понял,зачем мы это делаем?Можете объяснить?Это связано с маршрутизируемым событием???Если да,то почму?
А тему я не стал создавать в отдельной ветке,т.к. здесь вопрос скорее по C#,чем по разметке.
Спасибо! smile 

Автор: Idsa 20.6.2008, 17:35
Цитата(fromrus @  20.6.2008,  21:26 Найти цитируемый пост)
а с объявлением на уровне класса все норм-и так работает smile 

Работать-то работает, но функциональности не добавляет. А делать переменную с уровнем доступа, большим, чем ей нужен, - неправильно.

Цитата(fromrus @  20.6.2008,  21:26 Найти цитируемый пост)
Я правда не понял,зачем мы это делаем?Можете объяснить?Это связано с маршрутизируемым событием???Если да,то почму?

Нет, это никак не связано ни с маршрутизацией событий, ни с какими-либо другими фишками WPF.
Дело в том, что Вы в методе-обработчике клика обращались к переменной уровня класса b, которая содержала ссылку на кнопку, созданную при последней итерации foreach в DisplayLocalDrives. Это неверный подход. Обычно в ситуации, когда множеству объектов одного типа для обработки события передается делегат на один и тот же метод, для того, чтобы выяснить, какой же объект вызвал событие, используется параметр sender, приведенный к типу объекта.

Автор: fromrus 20.6.2008, 17:56
[QUOTE=Idsa,20.6.2008,  17:35]
Цитата(fromrus @  20.6.2008,  21:26 Найти цитируемый пост)
.... Обычно в ситуации, когда множеству объектов одного типа для обработки события передается делегат на один и тот же метод, для того, чтобы выяснить, какой же объект вызвал событие, используется параметр sender, приведенный к типу объекта.

Т.е. привидение нуна делать,когда много объектов->один делегат->один метод.Так?
Если можно дайте плиз инфу по этому вопросу или ссылку почитать,или книжку,где прочитать,или,если вы совсем великодушны,не могли бы написать пример,демонстрирующий данную проблему.Спасибо!

Автор: Idsa 20.6.2008, 18:03
Цитата(fromrus @  20.6.2008,  21:56 Найти цитируемый пост)
Т.е. привидение нуна делать,когда много объектов->один делегат->один метод.Так?

Необязательно. Приведение нужно делать, когда Вам нужна дополнительная информация об объекте-источнике события (помимо той, которая находится в EventArgs). Хотя если Вы знаете, например, что этот метод - обработчик события клик определенной кнопки, то можете не использовать sender, а напрямую обращаться к этой кнопке this.button1. Так что, действительно, наибольший смысл использование sender имеет тогда, когда один обработчик используется для событий нескольких объектов.

Цитата(fromrus @  20.6.2008,  21:56 Найти цитируемый пост)
Если можно дайте плиз инфу по этому вопросу или ссылку почитать,или книжку,где прочитать

Я в книгах по WinForms встречал такие примеры.

Цитата(fromrus @  20.6.2008,  21:56 Найти цитируемый пост)
если вы совсем великодушны,не могли бы написать пример,демонстрирующий данную проблему

Так Вы сами его и написали. Чуть выше smile

Автор: fromrus 20.6.2008, 18:11
один обработчик-несколько объектов,спасибо,на днях у петцольда поищу.

Автор: Kefir 20.6.2008, 18:15
fromrus, скажем так, приведение тут вообще не при чём. смысл всего этого предельно просто - допустим, у тебя есть энное количество кнопок (с разными надписями на них). Ты хочешь сделать так, чтобы при нажатии на кнопку появлялся месседж бокс с содержимым кнопки. Как это сделать? Первый вариант - объявить для каждой кнопки поле в классе (к примеру - button1, button2, ... button50) и для каждой кнопки в отдельности писать обработчик события Click:
Код

void buton1_Click(object sender, RoutedEventArgs e) {
  MessageBox.Show(this.button1.Content.ToString());
}
void buton2_Click(object sender, RoutedEventArgs e) {
  MessageBox.Show(this.button2.Content.ToString());
}
// ...

void buton50_Click(object sender, RoutedEventArgs e) {
  MessageBox.Show(this.button50.Content.ToString());
}

Однако, сразу становится ясно, что это - бед сивой кобылы - один и тот же код повторяется 50 раз. Поэтому можно написать этот обработичик следующим образом и всем 50-ти кнопкам поставить его на событие Click. 
Код

void buton50_Click(object sender, RoutedEventArgs e) {
  MessageBox.Show((sender as Button).Content.ToString());
}

sender содержит в себе ссылку на ту кнопку, которая послала событие Click. то есть если нажали на button1, то в sender будет button1 и так далее. Вот.

Если же интересует почему у sender тип object, то это лишь потому, что там может быть какой-угодно класс - не только Button, но так как мы знаем, что там у нас только кнопки, то можем приводить sender к Button. вот так.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)