【问题标题】:XAML Textbox readonly if a bool value is false如果布尔值为 false,则 XAML 文本框只读
【发布时间】:2017-06-27 14:57:53
【问题描述】:

我的 xaml 中有一个用于从数据源中提取的 ID 字段的文本框。我希望这个值是只读的,这样用户就不能改变这个值。我有一个按钮供用户添加一个新对象,这当然需要一个 ID。我的对象“IsNew”上有一个布尔属性,当用户单击“添加新”按钮时,该属性设置为 true。单击该按钮时,我希望此文本框可编辑。所以基本上,当“IsNew = true”时。我怎样才能做到这一点?

//My Xaml for the textbox:
 <TextBox x:Name="ID"
                 Text="{Binding SelectedRecord.ID}"/>

//Xaml for the button
<Button x:Name="AddNewRecordButton"
              Grid.Column="1"
              Margin="20,0,5,0"
              HorizontalAlignment="Left"
              VerticalAlignment="Bottom"
              HorizontalContentAlignment="Left"
              Height="24"
              Width="90"
              Command="{Binding AddNewRecordCommand}"
              CommandParameter="{Binding ElementName=window}"/>

//Code for the command method
 public void AddNewRecord(object parameter)
{
  var newRecord = new StockingReason();
  Records.Add(newRecord);
  SelectedRecord = newRecord;
  newRecord.IsNew = true;
  var control = parameter as IFocusable;
  control?.SetFocus();
}

【问题讨论】:

  • 可以只做一个快速的数据触发器,或者直接绑定到纯 xaml 中的切换按钮 IsChecked 属性,或者像下面显示的彼得这样的值转换器。有多种方法可以做到这一点。
  • 编辑了我的答案
  • 请随意为这两个答案投票

标签: wpf xaml


【解决方案1】:

使用IValueConverter

public class InvertBoolConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        bool val = (bool)value;
        return !val;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        bool val = (bool)value;
        return !val;
    }
}

举例说明如何使用

<Window x:Class="WpfApp1.MainWindow"
        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"
        xmlns:metro="http://metro.mahapps.com/winfx/xaml/controls"
        xmlns:local="clr-namespace:WpfApp1"
        xmlns:converter="clr-namespace:WpfApp1.MYCONVERTERNAMESPACE"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <converter:InvertBoolConverter x:Key="InvertBoolConverter" />
    </Window.Resources>

    <TextBox x:Name="ID"
             Text="{Binding SelectedRecord.ID}"
             IsReadOnly="{Binding IsNew, Converter={StaticResource InvertBoolConverter}}"/>

</Window>

【讨论】:

    【解决方案2】:

    您可以使用 IValueConverter,正如 Peter 指出的那样,
    或者您也可以使用 WPF 触发器

    WPF Trigger binding to MVVM property

    <TextBox>
            <TextBox.Style>
                <Style>
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding IsNew}" Value="True">
                            <Setter Property="TextBox.IsReadOnly" Value="False" />
                        </DataTrigger>
                        <DataTrigger Binding="{Binding IsNew}" Value="False">
                            <Setter Property="TextBox.IsReadOnly" Value="True" />
                        </DataTrigger>
    
                        <Trigger Property="Control.IsMouseOver" Value="true">
                            <Setter Property="Control.FontStyle" Value="Italic"></Setter>
                            <Setter Property="Control.Foreground" Value="Red"></Setter>
                            <Setter Property="Control.Background" Value="Yellow"></Setter>
                        </Trigger>
                        <Trigger Property="Button.IsPressed" Value="true">
                            <Setter Property="Control.Foreground" Value="Green"></Setter>
                            <Setter Property="Control.Background" Value="Blue"></Setter>
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </TextBox.Style>
        </TextBox>
    

    【讨论】:

    • 请注意,DataTrigger for Value="False" 是多余的,因为它只设置 IsReadOnly 属性的默认值。也不清楚为什么在这里显示其他触发器。最后,如果将 Style 的 TargetType 设置为 Button,则不必编写限定的属性名称。也就是说,我总是更喜欢触发器而不是价值转换器。无需额外上课。
    【解决方案3】:

    所以我想如果我理解您的要求,您可以执行以下操作:

    1) 确保您的代码所在的类实现了 INotifyPropertyChanged

    2) 在您的类上创建一个名为 IsRecordReadOnly 的新布尔属性,并让它转发所选记录 IsNew 标志的倒数。

    3) 更新所选记录后,在新布尔属性上调用 propertychanged。

    4) 将文本框上的 IsReadOnly 属性绑定到 ViewModel 中的 IsRecordReadOnly 属性。

    public class ViewModel : INotifyPropertyChanged
        {
        private IRecord _selectedRecord;
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    
        public List<IRecord> Records  = new List<IRecord>();
    
        public IRecord SelectedRecord
        {
            get { return _selectedRecord; }
            set
            {
                _selectedRecord = value;
                OnPropertyChanged();
            }
        }
    
        public bool IsRecordIdReadOnly
        {
            get { return !_selectedRecord?.IsNew ?? true; }
        }
    
        public void AddNewRecord(object parameter)
        {
            var newRecord = new StockingReason();
            Records.Add(newRecord);
            SelectedRecord = newRecord;
            newRecord.IsNew = true;
            var control = parameter as IFocusable;
            control?.SetFocus();
        }
    }
    

    然后你的 xaml...

    <Grid>
        <TextBox x:Name="ID"
                 Text="{Binding SelectedRecord.ID}" IsReadOnly="{Binding IsRecordReadOnly}"/>
    </Grid>
    

    【讨论】:

    • Peter 上面提到了使用转换器,我倾向于使用这些将业务逻辑值转换为基于 UI 的东西,例如颜色或可见性,但许多人更喜欢使用它们来满足您的需求。
    • return !_selectedRecord?.IsNew ?? true; - 简短且“可读”的代码 ;)
    猜你喜欢
    • 2018-05-09
    • 1970-01-01
    • 1970-01-01
    • 2010-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-10
    相关资源
    最近更新 更多