I made a rule validation on a property. What I want is that when the condition is not satisfied the save button should be disabled. How can I do it? This is how it looks:
image. The button is not disabled.
This is my code
public string FieldName { get; set; }
public Regex regularExpression = new Regex("^[a-zA-Z]*$");
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
//var stringValue = value as string;
if (FieldName == "Age")
return AgeValidate(value);
if (FieldName == "Name")
return NameValidation(value);
return new ValidationResult(true, null);
}
private ValidationResult NameValidation(object value)
{
var onlyCharacters = regularExpression.IsMatch((string)value);
if (onlyCharacters)
return new ValidationResult(true, null);
else
return new ValidationResult(false, $"Only alfabetical charaters");
}
In XAML:
<Label Grid.Row="0" Grid.Column="0" Margin="103,0,98,10" Content="Name : " VerticalContentAlignment="Center" HorizontalContentAlignment="Right" Grid.ColumnSpan="2"/>
<TextBox Grid.Row="0" Grid.Column="1" x:Name="txtCourtName" Margin="113,4,-55,6" VerticalContentAlignment="Center" HorizontalContentAlignment="Left" Validation.ErrorTemplate="{StaticResource errorTemplate}" >
<TextBox.Text>
<Binding Path="CourtName" ValidatesOnDataErrors="true" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local11:AuthValidation FieldName="Name"></local11:AuthValidation>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Button:
<Button Content="Save" x:Name="btnSaveCourt" Command="{Binding SaveCourt}" Margin="2,10" Width="119" Background="#909090" Foreground="Black" BorderBrush="White" />
And my template for error:
<ControlTemplate x:Key="errorTemplate">
<Border BorderBrush="OrangeRed" BorderThickness="2">
<Grid>
<AdornedElementPlaceholder>
<TextBlock Text="{Binding [0].ErrorContent}" Foreground="OrangeRed"
VerticalAlignment="Center" HorizontalAlignment="Right"
Margin="-220"
>
</TextBlock>
</AdornedElementPlaceholder>
</Grid>
</Border>
</ControlTemplate>
You could bind the button's IsEnabled to a property, a bool propfull (write propfull tab tab and it will write it out for you in visual studio).
Or you could use a MultiDataTrigger on the button and bind all the HasError property of the textboxes you want to validate.
Or if you use a command you could use the can execute property of it.
Related
I am doing a Xamarin form project, I have a list of tasks that can be completed or in completed. I want to add a button to display "Mark as completed" for in completed tasks and "Mark as in completed" for completed tasks. of course, it needs to refresh when the user clicks once.
I have a "CompletedDate" field that defines whether the task is completed when it has value.
TextColor="{StaticResource PrimaryBlack}" />
<CollectionView Grid.Row="1" ItemsSource="{Binding Tasks}" x:Name="List3">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Frame
Margin="0,10"
Padding="10"
BackgroundColor="{StaticResource PrimaryWhite}"
BorderColor="{StaticResource PrimaryLightGray}"
CornerRadius="10"
HasShadow="False">
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto" RowSpacing="15">
<StackLayout Orientation="Horizontal">
<Label
FontFamily="{StaticResource MeduimFont}"
Style="{StaticResource LabelMedium}"
Text="Completed"
IsVisible="{Binding CompletedTaskVisibility}}"
TextColor="{Binding PrimaryPersianGreen}" />
<StackLayout HorizontalOptions="EndAndExpand" Orientation="Horizontal">
<Image
HeightRequest="20"
Source="iconCalender.png"
WidthRequest="20" />
<Label
FontFamily="{StaticResource MeduimFont}"
Style="{StaticResource LabelMedium}"
Text="{Binding CompletedDate,StringFormat='{0:MMMM dd, yyyy}'}"
TextColor="{StaticResource PrimaryBlack}"
/>
</StackLayout>
</StackLayout>
<BoxView
Grid.Row="1"
HeightRequest="1"
Color="{StaticResource PrimaryLightGray}" />
<Label
Grid.Row="2"
Style="{StaticResource LabelMedium}"
Text="{Binding Name}"
TextColor="{StaticResource PrimaryBlack}" />
<Label
Grid.Row="3"
Style="{StaticResource LabelMedium}"
Text="{Binding Description}"
TextColor="{StaticResource PrimaryBlack}" />
<Button
x:Name="lstbtnMarkasComplite"
Grid.Row="5"
Padding="15,0"
Clicked="MarkTaskAsCompletedClicked"
CornerRadius="20"
FontSize="{StaticResource Font12}"
HeightRequest="40"
CommandParameter="{Binding Id}"
HorizontalOptions="CenterAndExpand"
Style="{StaticResource ButtonPurple}"
Text="Mark as Completed" />
</Grid>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
I have a "CompletedDate" field that defines whether the task is completed when it has value.
There are two solutions:
Create a boolean variable which checks the CompletedDate in your viewmodel and binding the Text to it. And then use a converter to convert the bool to string. About the clicked event, you can try to do the same thing as the Text or do a check in the click event in the page.cs.
Create a boolean variable in the view model too, but you need to create two button(one is "Mark as completed" and the other is "Mark as in completed") in the xaml and binding their IsVisible property to the boolean variable in the view model.
I suggest you to try the second solution, because the first one need to do a lot and convert the clicked event to a command. And the second one reduce much work.
Finally, you may try to use some other controls instead of the button. Such as:
CheckBox:https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/checkbox
Switch:https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/switch
Edit:
in the view model:
public ViewModel()
{
CompletedDate = ""; //set the value with the model data
if (CompletedDate != null)
{
isCompleted = true;
}
else isCompleted = false;
}
public event PropertyChangedEventHandler PropertyChanged;
bool isCompleted;
public bool IsCompleted {
get { return isCompleted; }
set {
isCompleted = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("IsCompleted"));
}
}
}
in the xaml:
<Button
x:Name="lstbtnMarkasComplite"
Grid.Row="5"
Padding="15,0"
Clicked="MarkTaskAsCompletedClicked"
CornerRadius="20"
FontSize="{StaticResource Font12}"
HeightRequest="40"
CommandParameter="{Binding Id}"
HorizontalOptions="CenterAndExpand"
Style="{StaticResource ButtonPurple}"
Text="Mark as Completed"
IsVisible="{Binding IsCompleted}"/>
<Button
x:Name="lstbtnMarkasComplite"
Grid.Row="5"
Padding="15,0"
Clicked="MarkTaskAsInCompletedClicked"
CornerRadius="20"
FontSize="{StaticResource Font12}"
HeightRequest="40"
CommandParameter="{Binding Id}"
HorizontalOptions="CenterAndExpand"
Style="{StaticResource ButtonPurple}"
Text="Mark as in Completed"
IsVisible="{Binding IsCompleted}/>
You can also use the DataTrigger just like Ibrennan208 sayed, and then check the Text value in the page.cs:
private async void Button_Clicked(object sender, EventArgs e)
{
Button button = sender as Button;
if(button.Text == "Mark as Completed")
{
.........
}
else
{
.........
}
}
Update:
<Button.Triggers>
<DataTrigger TargetType="Button" Binding="{Binding CompletedDate}" Value="{x:Null}">
<Setter Property="Text" Value="Mark Task as Completed"/>
</DataTrigger>
</Button.Triggers>
You could try adding a DataTrigger to your Button. They can be used to apply conditional bindings or values to properties such as the Button.Text property.
For a button you could implement it similarly to the following:
<Button Test="Mark Complete">
<Button.Triggers>
<DataTrigger TargetType="Button" Binding="{Binding IsCompleted}" Value="True">
<Setter Property="Text" Value="Mark Incomplete"/>
</DataTrigger>
</Button.Triggers>
</Button>
And then you could also add the IsCompleted field to your viewmodel:
public bool IsCompleted
get { return CompletedDate.HasValue; }
which would act as the switch for your button.
You would also need to add notification for the IsCompleted variable.
Calling OnPropertyChanged(nameof(IsCompleted)) when you set CompletedDate will do that.
TextBlock background color not changing.
I've bound my data to a TextBlock which updates with INotifyPropertyChanged, and the converter does fire.
public class Oddsindicator : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string myPrice = "0";
string tradePrice = "0";
var colorRed = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#FFB0E0E6");
var colorWhite = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("White");
var unchanged = new SolidColorBrush(colorRed);
var changed = new SolidColorBrush(colorGreen);
if (values[0] != DependencyProperty.UnsetValue)
{
myPrice = values[0].ToString();
tradePrice = values[1].ToString();
}
if (myPrice == tradePrice)
{
return unchanged;
}
else
{
return changed;
}
}
}
XAML:
<Window.Resources>
<local:Oddsindicator x:Key="Oddsindicator" >
</local:Oddsindicator>
</Window.Resources>
<TextBlock Text="{Binding BackPrice, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" TextAlignment="Center" Margin="1" Grid.Row="4" Grid.Column="2" />
<TextBlock>
<TextBlock.Background>
<MultiBinding Converter="{StaticResource Oddsindicator}">
<Binding Path="BackPrice"/>
<Binding Path="Lasttradedprice" />
</MultiBinding>
</TextBlock.background>
</TextBlock>
I've used break points at the return and they both fire. My bound value updates perfectly. The converters comparing the values and giving the correct results, just not updating the TextBlock.
(This should in fact be a comment but I need the formatting features of an answer)
You have two TextBlocks. The second (for which you set the background) has no Text and is probably of 0 size.
Try putting the TextBlock.Background in the first TextBlock:
<TextBlock Text="{Binding BackPrice, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" TextAlignment="Center" Margin="1" Grid.Row="4" Grid.Column="2" >
<TextBlock.Background>
<MultiBinding Converter="{StaticResource Oddsindicator}">
<Binding Path="BackPrice"/>
<Binding Path="Lasttradedprice" />
</MultiBinding>
</TextBlock.Background>
</TextBlock>
How i can get items as string in item view modal.
I have tried the following but they are not giving me correct output.
if (Opf.ShowDialog() == true)
{
StreamWriter swa = new StreamWriter(Opf.FileName);
using (swa)
{
for (int i = 0; i < PlayList.Items.Count; i++)
{
var ix = PlayList.Items.GetItemAt(i).ToString();
swa.WriteLine(ix);
}
}
MessageBox.Show("List Saved.");
}
It gives me
Wss.ItemViewModal
Wss.ItemViewModal
Wss.ItemViewModal
Wss.ItemViewModal
How i can get item from my listbox. My listbox xaml code
<ListBox Name="PlayList" Margin="0,50,0,30" Style="{DynamicResource ListBoxStyle1}" Background="Transparent" BorderThickness="0" Foreground="White" ItemsSource="{Binding Items, Mode=TwoWay}" MouseDoubleClick="PlayList_MouseDoubleClick">
<!--Style="{DynamicResource ListBoxStyle1}" SelectionChanged="PlayList_SelectionChanged"-->
<ListBox.ItemTemplate >
<DataTemplate DataType="{x:Type local:ItemViewModel}">
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type Label}">
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</Grid.Resources>
<Label Content="{Binding Sname}" FontSize="20" Foreground="White" x:Name="SongNameList" Margin="0" HorizontalAlignment="Left" Width="193"/>
<Label Content="{Binding Duration}" FontSize="14" HorizontalContentAlignment="Center" Foreground="Orange" x:Name="DurationList" Margin="189,0,0,0" HorizontalAlignment="Left" Width="62"/>
<Label Content="{Binding Isvid}" FontSize="20" HorizontalContentAlignment="Right" Foreground="DeepPink" x:Name="VideoC" Margin="0,0,300,0" HorizontalAlignment="Right" Width="55"/>
<Label Content="{Binding Format }" FontSize="12" HorizontalContentAlignment="Right" Foreground="Orange" x:Name="Format" Margin="0,0,220,0" HorizontalAlignment="Right" Width="50"/>
<Label Content="{Binding YTL}" FontSize="20" HorizontalContentAlignment="Right" Foreground="White" x:Name="YT" Margin="0,0,100,0" HorizontalAlignment="Right" Width="148"/>
<Label Content="{Binding SNN}" FontSize="20" HorizontalContentAlignment="Right" Foreground="SkyBlue" x:Name="SN" Margin="0" HorizontalAlignment="Right" Width="95"/>
<Label Content="{Binding VPath }" FontSize="20" Foreground="Green" x:Name="Path" Margin="256,0,332,0"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Anything more you want to ask just comment.
Thank you very much .
You can override the method ToString() of your ItemViewModel class.
public class ItemViewModel
{
...
public override string ToString()
{
return $"ItemViewModel: {Sname} {Duration} {Isvid} {Format } {YTL} {SNN} {VPath}";
}
...
}
if not overriden in class, ToString() returns class name with namespace which heppen to be Wss.ItemViewModal. Overriding ToString() for export purposes is hardly optimal solution - export formats can vary for a single class! It makes more sense to implement IFormattable in ItemViewModal and specify format.
Alternatively: don't use ToString and list all properties which should be exported:
if (Opf.ShowDialog() == true)
{
using (StreamWriter swa = new StreamWriter(Opf.FileName))
{
foreach(ItemViewModal vm in PlayList.Items)
{
var ix = vm.Sname + " " + vm.Duration;
swa.WriteLine(ix);
}
}
MessageBox.Show("List Saved.");
}
I have 10 textboxes and I want there addition to be shown in a single textblock on lost_focus UpdateSourceTrigger property.
If you need to update the sum on TextBox lost focus event you can use classic events. This is the XAML (I used just four TextBoxes, but it is easy to extend):
<StackPanel>
<TextBox Name="txt01" Margin="3" HorizontalAlignment="Stretch" LostFocus="txt_LostFocus" TextChanged="txt_TextChanged" />
<TextBox Name="txt02" Margin="3" HorizontalAlignment="Stretch" LostFocus="txt_LostFocus" TextChanged="txt_TextChanged" />
<TextBox Name="txt03" Margin="3" HorizontalAlignment="Stretch" LostFocus="txt_LostFocus" TextChanged="txt_TextChanged" />
<TextBox Name="txt04" Margin="3" HorizontalAlignment="Stretch" LostFocus="txt_LostFocus" TextChanged="txt_TextChanged" />
<TextBlock Name="sum" Margin="3,10,3,3" />
</StackPanel>
In the code you have the event handlers:
private void txt_LostFocus(object sender, RoutedEventArgs e)
{
int value1;
int value2;
TextBox textBox = (TextBox)sender;
if (textBox.Tag is bool && (bool)textBox.Tag)
{
if (Int32.TryParse(textBox.Text, out value1))
{
if (String.IsNullOrEmpty(sum.Text))
{
sum.Text = textBox.Text;
}
else
{
Int32.TryParse(sum.Text, out value2);
sum.Text = Convert.ToString(value1 + value2);
}
}
textBox.Tag = false;
}
}
private void txt_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = (TextBox)sender;
textBox.Tag = true;
}
On the other side, if you can give up the "LostFocus" requirement, you can use MultiBinding (in this case it works just in "PropertyChanged mode", since TextBoxes are now the sources):
<StackPanel>
<TextBox Name="txt01" Margin="3" HorizontalAlignment="Stretch" />
<TextBox Name="txt02" Margin="3" HorizontalAlignment="Stretch" />
<TextBox Name="txt03" Margin="3" HorizontalAlignment="Stretch" />
<TextBox Name="txt04" Margin="3" HorizontalAlignment="Stretch" />
<TextBlock Name="sum" Margin="3,10,3,3">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource AddValueConverter}" Mode="OneWay">
<MultiBinding.Bindings>
<Binding ElementName="txt01" Path="Text" />
<Binding ElementName="txt02" Path="Text" />
<Binding ElementName="txt03" Path="Text" />
<Binding ElementName="txt04" Path="Text" />
</MultiBinding.Bindings>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
You just need to write a simple converter:
public class AddValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
int sum = 0;
int result;
foreach(object value in values)
{
if (Int32.TryParse(System.Convert.ToString(value), out result))
{
sum += result;
}
}
return System.Convert.ToString(sum);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
I have a listbox which stores records of XML file. My XMLfile has multiple elements like Name, Destination, employee ID. The listbox would display all the name contents of the XMLfile.
<Information>
<Name>Goutham</Name>
<Destination>dar</Destination>
<EmployeeID>da</EmployeeID>
</Information>
<Information>
<Name>Adam</Name>
<Destination>ads</Destination>
<EmployeeID>dwa</EmployeeID>
</Information>
<Information>
<Name>dwad</Name>
<Destination>wadwa</Destination>
<EmployeeID>daw</EmployeeID>
</Information>
The listbox displays all the different names like Goutham,Adam,dwad.
Now if I select Goutham in the listbox, I need to display all the details of Goutham on the textbox. How do i do it?
This is my xaml file
<ListBox Height="251" HorizontalAlignment="Left" Margin="330,23,0,0" Name="listBox1" VerticalAlignment="Top" Width="170" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Name" ItemsSource="{Binding}"/>
<TextBox Height="23" HorizontalAlignment="Left" Margin="141,42,0,0" Name="textBox1" VerticalAlignment="Top" Width="173" Text="{Binding ElementName= listbox1, Path=SelectedItem.Name}"/>
If the listbox fills up it should allready work, you just have a typo in the ElementName binding it should be listBox1.
Also if you want all the details not just the Name you could make a converter and leave only SelectedItem in the binding path. The converter would then format the details form the Information instance returning a string to be displayed in the texbox, or just use several textboxes.
Another option is to have MultiBinding with StringFormat:
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{0} {1} {2}">
<Binding ElementName="listbox1", Path="SelectedItem.Name" />
<Binding ElementName="listbox1", Path="SelectedItem.Destination" />
<Binding ElementName="listbox1", Path="SelectedItem.EmployeeID" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
You can bind ListBox to once specific property say Name & then bind TextBlock to this listbox. Depending upon your need, you can have individual TextBlocks for each property or a single TextBlock... as shown below...
<Window x:Class="ListBoxDataBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:ListBoxDataBinding"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<c:EmployeeConverter x:Key="myEmployeeConverter"/>
</Window.Resources>
<StackPanel Orientation="Vertical">
<ListBox x:Name="lbEmployee">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Width="248" Height="24" Text="Employee Name" />
<TextBlock Grid.Row="0" Grid.Column="1" Width="248" Height="24" Text="{Binding ElementName=lbEmployee, Path=SelectedItem.Name}" />
<TextBlock Grid.Row="1" Grid.Column="0" Width="248" Height="24" Text="Employee EmployeeId" />
<TextBlock Grid.Row="1" Grid.Column="1" Width="248" Height="24" Text="{Binding ElementName=lbEmployee, Path=SelectedItem.EmployeeId}" />
<TextBlock Grid.Row="2" Grid.Column="0" Width="248" Height="24" Text="Employee Destination" />
<TextBlock Grid.Row="2" Grid.Column="2" Width="248" Height="24" Text="{Binding ElementName=lbEmployee, Path=SelectedItem.Destination}" />
<TextBlock Grid.Row="3" Grid.Column="0" Width="248" Height="24" Text="Employee Details" />
<TextBlock Grid.Row="3" Grid.Column="2" Width="248" Height="24">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource myEmployeeConverter}" ConverterParameter="FormatEmployee">
<Binding ElementName="lbEmployee" Path="SelectedItem.Name" />
<Binding ElementName="lbEmployee" Path="SelectedItem.EmployeeId" />
<Binding ElementName="lbEmployee" Path="SelectedItem.Destination" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
</StackPanel>
Code behind....
public partial class MainWindow : Window
{
private List<Employee> _lstEmployees;
public MainWindow()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
_lstEmployees = new List<Employee>();
_lstEmployees.Add(new Employee { Name = "Goutham", EmployeeId = "da", Destination = "dar" });
_lstEmployees.Add(new Employee { Name = "Adam", EmployeeId = "dwa", Destination = "ads" });
_lstEmployees.Add(new Employee { Name = "dwad", EmployeeId = "daw", Destination = "wadwa" });
lbEmployee.ItemsSource = _lstEmployees;
}
}
public class EmployeeConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string name;
switch ((string)parameter)
{
case "FormatEmployee":
if (values[0] is String)
{
name = values[0] + ", " + values[1] + ", " + values[2];
}
else
{
name = String.Empty;
}
break;
default:
name = String.Empty;
break;
}
return name;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
string[] splitValues = ((string)value).Split(' ');
return splitValues;
}
}