1

Вот мой DialogHost

<MaterialDesign:DialogHost Identifier="RenameDialog" Style="{StaticResource MaterialDesignEmbeddedDialogHost}" Panel.ZIndex="2002" DialogMargin="0" Grid.ColumnSpan="3" Grid.RowSpan="2" Focusable="False" IsManipulationEnabled="True" FocusVisualStyle="{x:Null}">
            <MaterialDesign:DialogHost.DialogContentTemplate>
                <DataTemplate>
                    <Grid Height="150" Width="300" FocusVisualStyle="{x:Null}" >
                        <MaterialDesign:ColorZone Content="{Binding Header}" Height="50" Mode="PrimaryLight" VerticalContentAlignment="Center" HorizontalContentAlignment="Center"/>
                        <TextBox Text="{Binding Text, Mode=OneTime}" Margin="15 50 15 0" VerticalAlignment="Top" Style="{StaticResource MaterialDesignFloatingHintTextBox}"></TextBox>
                        <Button x:Name="Dialog3" Style="{StaticResource MaterialDesignFlatButton}" Content="Отмена" Margin="0 0 67 8" VerticalAlignment="Bottom" HorizontalAlignment="Right" FocusVisualStyle="{x:Null}" Command="MaterialDesign:DialogHost.CloseDialogCommand" CommandParameter="Cancel" />
                        <Button x:Name="Dialog4" Style="{StaticResource MaterialDesignFlatButton}" Content="OK"     Margin="0 0 8 8"  VerticalAlignment="Bottom" HorizontalAlignment="Right" FocusVisualStyle="{x:Null}" Command="MaterialDesign:DialogHost.CloseDialogCommand" CommandParameter="OK" />
                    </Grid>
                </DataTemplate>
            </MaterialDesign:DialogHost.DialogContentTemplate>
        </MaterialDesign:DialogHost>

Вот-так я его вызываю:

DialogHost.Show(new { Header = "Header", Text = "Text" }, "RenameDialog");

Как сделать так чтобы, я мог третим параметром указывать обработчик TextChanged для TextBox?
Что-то типа такого

void TextBox_TextChanged(object NameTablesender, TextChangedEventArgs e)
{

}

DialogHost.Show(new { Header = "Header", Text = "Text", Changer = TextBox_TextChanged}, "RenameDialog");
A K
  • 28,718
Qwerty
  • 165
  • 1
    Прибиндить Text к свойству с установкой UpdateSourceTrigger=PropertyChanged и вызывать свой код из сеттера. – aepot Dec 26 '20 at 20:26
  • @aepot Можете показать пример, а то я не очень понял как это сделать. Буду благодарен. – Qwerty Dec 26 '20 at 20:33
  • К сожалению сейчас набегами здесь, ищите информацию про привязку данных, вам нужно назначить DataContext и привязаться к свойству {Binding ИмяСвойства, UpdateSourceTrigger=PropertyChanged}. Примеров полинтернета. – aepot Dec 26 '20 at 20:37
  • Еще привязки, я так могу долго продолжать. Здесь только моих ответов с использованием привязок штук 30, а сколько их всего, я даже предположить боюсь. – aepot Dec 26 '20 at 22:52
  • @aepot А можно решить эту задачу через EventSetter или EventTrigger? – Qwerty Dec 26 '20 at 22:55
  • Можно, а зачем? Это как минимум не проще сделать. – aepot Dec 26 '20 at 22:55
  • @aepot Ну я хотел бы засунуть все в Wpf без MVVM – Qwerty Dec 26 '20 at 22:57
  • @aepot А можете продемонстрировать как, а то пробовал не вышло. – Qwerty Dec 26 '20 at 23:02
  • MVVM тут вообще не при чем. Я вам про привязки данных, которые к MVVM не имеют никакого отношения. – aepot Dec 26 '20 at 23:06
  • @aepot Так вы так и не использовали EventSetter, EventTrigger. – Qwerty Dec 26 '20 at 23:17
  • Если уж вам так нужен этот обработчик, почему его просто в xaml не прописать? – aepot Dec 26 '20 at 23:18
  • А зачем мне EventTrigger? Я их вообще никогда за ненадобностью не использую. Все интерфейсные кастомизации можнро прописать с помощью стиля со Style.Triggers прямо в XAML. – aepot Dec 26 '20 at 23:22
  • А вообще изучайте привязки данных и MVVM, без этих навыков вы только запутаетесь. Чем дальше, тем сложнее. WPF без привязок - вообще ад кромешный, а с использованием MaterialDesign - еще хуже. Как я понял, ответ ниже вам не подошел - удаляю. – aepot Dec 26 '20 at 23:25
  • А вообще фраза Binding для собития звучит как Скакалка для сковородки, совершенно никак не связанные друг с другом вещи. Отсюда вывод, вам надо изучить, что такое событие, и что такое и как работает Binding. – aepot Dec 26 '20 at 23:28

1 Answers1

3

Давайте я покажу вам код, который имел в виду @aepot.

Вам нужно определить аргумент так:

class DialogArgs : VM
{
    public DialogArgs(Action<string> textChangeCallback) =>
        this.textChangeCallback = textChangeCallback ??
                throw new ArgumentNullException(nameof(textChangeCallback));
string header;
public string Header
{
    get =&gt; header;
    set =&gt; Set(ref header, value);
}

string text;
public string Text
{
    get =&gt; text;
    set
    {
        if (Set(ref text, value))
            textChangeCallback(text);
    }
}

private readonly Action&lt;string&gt; textChangeCallback;

}

Базовый класс VM с реализацией INotifyPropertyChanged вам пригодится и в других местах:

class VM : INotifyPropertyChanged
{
    protected bool Set<T>(ref T field, T value,
                         [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, value))
            return false;
    field = value;
    RaisePropertyChanged(propertyName);
    return true;
}

protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) =&gt;
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

public event PropertyChangedEventHandler PropertyChanged;

}

а вместо Text="{Binding Text, Mode=OneTime}" написать Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}"

Ну и показ диалога при этом будет выглядеть так:

DialogHost.Show(
    new DialogArgs(callback) { Header = "Header", Text = "Text" },
   "RenameDialog");

(например, я пробовал вместо callback написать t => Debug.WriteLine(t), так что изменённый текст выводился в окошко Output).


Дополнение по результатам обсуждения в комментариях. Чтобы показывать ошибочный или неошибочный выбор имени, вам нужно добавить ещё пару свойств в DialogArgs:

    bool isGood;
    public bool IsGood
    {
        get => isGood;
        set => Set(ref isGood, value);
    }
string errorText;
public string ErrorText
{
    get =&gt; errorText;
    set =&gt; Set(ref errorText, value);
}

В вашем диалоге вы привязываете цвета к IsGood через конвертер (или пользуетесь триггером), и выводите текст из ErrorText в качестве сообщения об ошибке. А код вашего callback'а должен проверять имя, и устанавливать IsGood и ErrorText.

Например, так. Вот конвертер:

class HintColorConverter : IValueConverter
{
    public object Convert(object value, Type t, object parameter, CultureInfo ci) =>
        value is true ? Brushes.Black : Brushes.Red;
    public object ConvertBack(object value, Type t, object parameter, CultureInfo ci) =>
        throw new NotSupportedException();
}

Textbox в коде диалога:

<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" Margin="15 50 15 0" VerticalAlignment="Top"
         Style="{StaticResource MaterialDesignFloatingHintTextBox}"
         Foreground="{Binding IsGood, Converter={StaticResource HintColorConverter}}"
         materialDesign:HintAssist.Foreground="{Binding IsGood, Converter={StaticResource HintColorConverter}}"
         materialDesign:TextFieldAssist.UnderlineBrush="{Binding IsGood, Converter={StaticResource HintColorConverter}}">

и в начала DataTemplate, конечно,

<DataTemplate.Resources>
    <local:HintColorConverter x:Key="HintColorConverter"/>
</DataTemplate.Resources>

Вот такое хитрое создание DialogArgs (считаем чётную длину правильной):

DialogArgs args = null;
args = new DialogArgs(t => args.IsGood = t.Length % 2 == 0) { Header = "Header" };
args.Text = "Text";
await DialogHost.Show(args, "RenameDialog");

Результат:

в общем не особо хитро, асинхронная обработка была бы поинтереснее

Можно и через триггеры, тогда вам не нужен конвертер:

<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" Margin="15 50 15 0" VerticalAlignment="Top">
    <TextBox.Style>
        <Style BasedOn="{StaticResource MaterialDesignFloatingHintTextBox}" TargetType="TextBox">
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsGood}">
                    <DataTrigger.Value><sys:Boolean>False</sys:Boolean></DataTrigger.Value>
                    <DataTrigger.Setters>
                        <Setter Property="Foreground" Value="Red"/>
                        <Setter Property="materialDesign:HintAssist.Foreground" Value="Red"/>
                        <Setter Property="materialDesign:TextFieldAssist.UnderlineBrush" Value="Red"/>
                    </DataTrigger.Setters>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TextBox.Style>
</TextBox>

(не забудьте где-то вверху xmlns:sys="clr-namespace:System;assembly=mscorlib")

Если проверка длинная, сделайте её асинхронной.

VladD
  • 206,799
  • А где определять сам TextChanged? – Qwerty Dec 26 '20 at 21:04
  • Где захотите, лишь бы его сигнатура была void f(string s). – VladD Dec 26 '20 at 21:07
  • Как Такое реализовать void TextBox_TextChanged(object NameTablesender, TextChangedEventArgs e) { TextBox text = (TextBox)NameTablesender; text.SetValue(HintAssist.ForegroundProperty, Brushes.Red); text.SetValue(HintAssist.ForegroundProperty, Brushes.Red); text.SetValue(TextFieldAssist.UnderlineBrushProperty, Brushes.Red); text.Foreground = Brushes.Red; – Qwerty Dec 26 '20 at 21:13
  • @Qwerty если вы еще не поняли, вам не нужен обработчик события, а просто метод с нужным вам кодом. – aepot Dec 26 '20 at 21:13
  • @aepot Я понял, но как изменять свойства Textbox при изменении текста? нету sender – Qwerty Dec 26 '20 at 21:15
  • @Qwerty: А какое именно свойство вы хотите изменить? Вам нужно не напрямую работать со свойствами UI-элементов (мы ж не в Winforms), а привязывать их через Binding. – VladD Dec 26 '20 at 21:20
  • @VladD Я выше некоторое перечислил – Qwerty Dec 26 '20 at 21:21
  • Хм, вы меняете визуальные свойства. Это не дело VM-класса, заниматься представлением. Расскажите свою задачу более высокого уровня: что именно нужно реализовать? – VladD Dec 26 '20 at 21:23
  • @VladD У меня есть Dialog Host в нем есть TextBox, при вызове в нем будет название таблицы, когда я изменяю текст то програма должна проверять можно ли сохранить такое название, если нельзя переименовать таблицу, то "ОК" дезактивируется. При нажатии на "ОК" таблица переименовывается. Ну и если есть ошибка в названии то под TextBox выводиться текст ошибки. – Qwerty Dec 26 '20 at 21:34
  • @Qwerty: Смотрите дополнение в ответе – VladD Dec 26 '20 at 21:40
  • @Qwerty но как изменять свойства Textbox при изменении текста - реализовать INotifyPropertyChanged. – aepot Dec 26 '20 at 21:42
  • @VladD А как привязать цвет к IsGood через конвертер (или пользуетесь триггером)? – Qwerty Dec 27 '20 at 17:37
  • @Qwerty: Дополнил ответ, смотрите в конце – VladD Dec 27 '20 at 18:04
  • @VladD А как через тригеры? – Qwerty Dec 27 '20 at 18:11
  • @Qwerty: Написал и через триггеры тоже. – VladD Dec 27 '20 at 18:26
  • @VladD Спасибо. – Qwerty Dec 27 '20 at 18:28
  • @Qwerty: Пожалуйста! – VladD Dec 27 '20 at 18:28