|
I've asked about this already, but I'm not really getting it. I'm asking this again with more detail in the hopes that it'll make more sense.
Lets say I develop a custom control that have a TextBlock over a TextBox[^]. I have defind colors for Normal and Mouse Over
Here's the XAML for my control. It's pretty simple:
<pre><ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyTextBoxEx">
<pre>
<SolidColorBrush x:Key="CaptionNormalColor" Color="Green"/>
<SolidColorBrush x:Key="CaptionMouseOverColor" Color="Purple"/>
<SolidColorBrush x:Key="TextNormalColor" Color="Red"/>
<SolidColorBrush x:Key="TextMouseOverColor" Color="Blue"/>
<Style TargetType="{x:Type local:TextBoxEx}">
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TextBoxEx}">
<StackPanel Orientation="Vertical">
<Border Padding="{Binding Padding, RelativeSource={RelativeSource TemplatedParent}}"
Margin="{Binding Margin, RelativeSource={RelativeSource TemplatedParent}}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<StackPanel Orientation="Vertical">
<TextBlock x:Name="caption"
Foreground="{StaticResource CaptionNormalColor}"
Text="{TemplateBinding Caption}"/>
<TextBox x:Name="text"
Foreground="{StaticResource TextNormalColor}"
Text="{TemplateBinding Text}"/>
</StackPanel>
</Border>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="caption" Property="TextElement.Foreground" Value="{StaticResource CaptionMouseOverColor}"/>
<Setter TargetName="caption" Property="TextElement.FontWeight" Value="Bold"/>
<Setter TargetName="text" Property="TextElement.Foreground" Value="{StaticResource TextMouseOverColor}"/>
<Setter TargetName="text" Property="TextElement.FontStyle" Value="Italic"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here's the question. I have assigned colors for normal, and for the Mouse Over triggers. Assume this control was in a compiled assembly, and I gave it to you, how would you go about overwriting the theme colors, or theming it the way you wanted?
When you buy a third party control, and want apply your theme to it, how do the controls inside get their colors set? In my case, the triggers are set to a specifically named brush. Just knowing the brush names wouldn't help you overwrite the theme colors, would it?
In theory, theory and practice are the same. But in practice, they never are.”
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
|
|
|
|
|
Making color changes to parts of controls that already have "part resources" defined in generic.xaml is not customizing; it is annoying.
"Before entering on an understanding, I have meditated for a long time, and have foreseen what might happen. It is not genius which reveals to me suddenly, secretly, what I have to say or to do in a circumstance unexpected by other people; it is reflection, it is meditation." - Napoleon I
|
|
|
|
|
You're missing the point.
When you buy a third party control, like say from Infragistics, and you use it in your app, when you write your own theme the third party control respects it.
I'm trying to understand how to make my control work the same way
In theory, theory and practice are the same. But in practice, they never are.”
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
|
|
|
|
|
I am implementing INotifyDataErrorInfo. Here's my base ViewModel
public class _ViewModelBase : BindableBase, INotifyDataErrorInfo
{
#region Events
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
#endregion
#region Private Fields
public readonly Dictionary<string, List<string>> _validationErrors = new Dictionary<string, List<string>>();
#endregion
#region Properties
public bool HasErrors => _validationErrors.Any();
#endregion
#region Public Methods
public IEnumerable GetErrors(string propertyName)
{
return _validationErrors.ContainsKey(propertyName) ? _validationErrors[propertyName] : null;
}
#endregion
#region Protected Methods
protected void AddError(string propertyName, string error)
{
if (!_validationErrors.ContainsKey(propertyName))
{
_validationErrors[propertyName] = new List<string>();
}
if (!_validationErrors[propertyName].Contains(error))
{
_validationErrors[propertyName].Add(error);
RaiseErrorsChanged(propertyName);
}
}
protected void ClearErrors(string propertyName)
{
if (_validationErrors.ContainsKey(propertyName))
{
_validationErrors.Remove(propertyName);
{
RaiseErrorsChanged(propertyName);
}
}
}
#endregion
#region Private Methods
private void RaiseErrorsChanged(string propertyName)
{
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
}
#endregion
}
Here's my CustomerView
<TextBlock Grid.Row="0"
Text="Customer Name"/>
<TextBox Grid.Row="1"
Grid.Column="0"
Text="{Binding CustomerName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
IsEnabled="{Binding AreFieldsEnabled}">
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder x:Name="textBox" />
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent}" Foreground="Red" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
</TextBox>
Here's my CustomerViewModel (parts ommited)
private void Validate()
{
ClearErrors(nameof(Customer.CustomerName));
if (string.IsNullOrWhiteSpace(Customer.CustomerName))
{
AddError(nameof(Customer.CustomerName), "The Customer Name cannot be empty.");
}
}
private void Customer_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
IsChanged = true;
Validate();
RaiseChangedEvent();
}
The problem is that if I use complex nation, as in Customer.CustomerName instead of just CustomerName, the validation text does not appear. The error gets added to the collection, but I don't see it.
What's wrong here?
In theory, theory and practice are the same. But in practice, they never are.”
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
|
|
|
|
|
That makes no sense - both nameof(Customer.CustomerName) and nameof(CustomerName) will compile to the constant string "CustomerName" .
Are you sure that the view-model property is spelled correctly?
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
OK, so I'm not really sure what the problem is here:
private void Validate()
{
ClearErrors("CustomerName");
if (string.IsNullOrWhiteSpace(Customer.CustomerName))
{
AddError("CustomerName", "The Customer Name cannot be empty.");
}
}
<TextBox Grid.Row="1"
Grid.Column="0"
Text="{Binding Customer.CustomerName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
IsEnabled="{Binding AreFieldsEnabled}">
<pre>
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder x:Name="textBox" />
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent}" Foreground="Red" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
The TextBox is bound to Customer.CustomerName. When I remove the text, the error is added, but I don't see anything. I'm guessing I'm not doing this right.
In theory, theory and practice are the same. But in practice, they never are.”
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
|
|
|
|
|
The errors don't match the property you're binding to.
Try using:
const string propertyName = $"{nameof(Customer)}.{nameof(Customer.CustomerName)}";
ClearErrors(propertyName);
f (string.IsNullOrWhiteSpace(Customer.CustomerName))
{
AddError(propertyName, "The Customer Name cannot be empty.");
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Being "in error" is the state of the object in question; not a flag or entry in a list. That's transaction oriented versus "online".
My "HasErrors" runs all the "validations" on the object and returns true or false anytime it is called (e.g. before saving). No "error lists" to clear etc.
You can also retrieve all errors (to show only the first, for example, to avoid overloading thee user).
At the same time, all the textboxes in error are "red lined" as expected.
"Before entering on an understanding, I have meditated for a long time, and have foreseen what might happen. It is not genius which reveals to me suddenly, secretly, what I have to say or to do in a circumstance unexpected by other people; it is reflection, it is meditation." - Napoleon I
|
|
|
|
|
See my latest reply to Richard
In theory, theory and practice are the same. But in practice, they never are.”
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
|
|
|
|
|
|
OK, I don't get what's wrong here.
Here's the CustomerName in my CustomerView
<TextBox Grid.Row="1"
Grid.Column="0"
Text="{Binding Customer.CustomerName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}"
IsEnabled="{Binding AreFieldsEnabled}">
<pre>
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder/>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent}" Foreground="Red" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
Here's the VM
private void Customer_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
IsChanged = true;
ValidateCustomerName();
}
private void ValidateCustomerName()
{
ClearErrors(nameof(Customer.CustomerName));
if (string.IsNullOrWhiteSpace(Customer.CustomerName))
{
AddError(nameof(Customer.CustomerName), "The Customer Name cannot be empty.");
}
}
When I clear out the CustomerName field, the ValidateCustomerName method fires, and the error is added to the errors collection. I just don't see the error text on the UI.
However, if instead of binding to Customer.CustomerName, if I put a string property on the VM called CustomerName and use that, then I see the error text.
In theory, theory and practice are the same. But in practice, they never are.”
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
|
|
|
|
|
I use INotifyDataErrorInfo as in this article[^].
This is a fine for validating individual textboxes.
- How would you validate that a list has items in it?
- I have 2 comboboxes with the same collection of items in it. How can I validate that they both have differt items selected?
In theory, theory and practice are the same. But in practice, they never are.”
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
modified 29-Sep-23 22:06pm.
|
|
|
|
|
Quote: GetErrors method returns an IEnumerable that contains validation errors for the specified property (when the propertyName parameter isn’t equal to null or empty string) or for the entire entity (when the propertyName parameter is equal to null or empty string)
If you want to validate multiple properties of your entity, you need to return the errors when the GetErrors method[^] is called with a null property name.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
I am creating a File Picker control, which will have a caption, a textbox, and a button with an image on it. Pretty straightforward. The control will live inside a controls project I have.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Marois.Framework.WPF.Controls">
<pre>
<SolidColorBrush x:Key="disabledBackgroundBrush" Color="Gray"/>
<Style TargetType="{x:Type local:MaroisFilePicker}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border x:Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding BorderThickness}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Caption}"
x:Name="caption"
Margin="2"/>
<TextBox Grid.Column="1"
x:Name="textbox"
Text="{Binding File}"/>
<Button Grid.Column="2"
x:Name="button"
Command="{Binding SelectedFileCommand}"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="button" Property="Background" Value="{StaticResource disabledBackgroundBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
If you notice, I have defined a background brush for the textbox disabled state.
So here's the question. All of this lives in my Marois.Framework.WPF.Controls library. When I use this in another app, how do I then define different colors for the controls? Do I need all of this xaml in my app? Or do I somehow reference each part of my control in a style?
In theory, theory and practice are the same. But in practice, they never are.”
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
|
|
|
|
|
I pass anything that needs custom ("team") colors, a "custom colors" (team) object at "load" time; with "primary" and "secondary" colors and FG, BG colors, that are bound to at run time. The other option is to use "dynamic" resource references; where you play with the resource (brush) that is referenced.
"Before entering on an understanding, I have meditated for a long time, and have foreseen what might happen. It is not genius which reveals to me suddenly, secretly, what I have to say or to do in a circumstance unexpected by other people; it is reflection, it is meditation." - Napoleon I
|
|
|
|
|
I don't understand that at all. Please explain
In theory, theory and practice are the same. But in practice, they never are.”
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
|
|
|
|
|
It's probably not "MVVM".
"Before entering on an understanding, I have meditated for a long time, and have foreseen what might happen. It is not genius which reveals to me suddenly, secretly, what I have to say or to do in a circumstance unexpected by other people; it is reflection, it is meditation." - Napoleon I
|
|
|
|
|
Huh? That's even momre confusing
In theory, theory and practice are the same. But in practice, they never are.”
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
|
|
|
|
|
I'm trying to create a ValueConverter in .Net Core 6, except I can't seem to find the System.Windows.Data namespace, where IValueConveter lives.
What am I missing??
In theory, theory and practice are the same. But in practice, they never are.”
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
|
|
|
|
|
|
Hi everybody,
I want to make an overlay above an app or capturing this app into my window. I think i will use an opengl library i thougth to work with
glfw but the feature that allow mouse passtrough is for the 3.4 version, and current is 3.3.8.
The best way for me were to run the window i want directly inside my own window, and control it for fullscreen and other things. I'm not habit with linux, i know on w10 a lot of possibilities were removed for security reason.
My work is on ArchLinux to be more accurate, i use lxde but if possible i prefer to avoid a "desktop environment"...
Thx !
|
|
|
|
|
I'm still trying to get my Navigation Control. The repo is here[^].
Thanks to @RichardDeeming for all your help so far. I wouldn't have made it this far without your help.
So the Navigataion Container contains one or more Navigation Panes. When a pane is expanded, I want its loading to begin after it is expanded. The pane should expand, then the user sees the circular progress indicator while the list is loading. Then, when the results are returned, the circular progress indicator should go away and the results displayed.
If you run the app now, and expand a section, you'll see that it doesn't expand until it's after it's done loading.
I could use another pair of eyes on this.
In theory, theory and practice are the same. But in practice, they never are.”
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
|
|
|
|
|
I have created a progress unit control that show a progress in my WPF application and it looks like this:
https://pasteboard.co/peNn9PiguyNC.png
The way I have done this is adding a ListView in my main application view that has a higher ZIndex than the other controls. This essentially overlays all controls, but does not react hit tests and is not visible.
<ListView x:Name="ProgressListView" Grid.Row="1" Grid.RowSpan="2" ItemsSource="{Binding ProgressCollection}" Background="Transparent" Panel.ZIndex="10" IsHitTestVisible="False">
</ListView>
I have most of the WPF code here, but if you need it, I can post it
This works as you can see from the picture. If you look at the picture, there is a close image which is implemented this way:
<Button Grid.Row="0" Grid.Column="1"
Content="{wpf:MaterialIconExt Kind=WindowClose}"
Command="{Binding CloseProgressCommand}"
Style="{StaticResource ProgressButtonStyle}" />
I would like to be able to click this button to close any progress unit, in case it blocks the users view of any controls beneath it. With my current implementation this is not possible as my ListView currently is marked with IsHitTestVisible="False" . That means all controls under it also ignores the hit test, even if I set the buttons IsHitTestVisible to true. That means the button cannot be clicked by the mouse.
How can I make the button work on my progress unit and still have them floating?
So far:
- I have experimented with overriding the hittest of the listview, so I can ignore hittest in code but allow the button to be pressed. This did not work, but I am still looking into this.
- I have tried fiddling with the ZIndex on the Button, but if the listview ZIndex is lower that the rest, then buttons inside it will of cause also be behind it, no matter the buttons ZIndex. Its a part of the ListView.
- I have also looked into the Adorner layer. I find that this could probably work, but it will require a complete rewrite of the logic and as I understand it is not really what it is meant for and will be very complicated.
Any suggestions is very welcome. I am 99% done, I just need the last little piece of the puzzle.
Solution/Workaround
So browsing some more and looking a bit into the links in the replies, I came up with a solution. I found it while browsing but to be honest i forgot where from. Anyway, the first thing i did was taking a part of @Gerry Schmitz advice and remove the z indexs. Then wil browsing @Richards supplied links for the n'th time i came a cross the workaround/solution.
I added a canvas as the first item and moved listview control in there. The canvas was then set to align content to the bottom and then i anchored the listview to the bottom of the canvas. Code looks like this:
<Canvas Grid.Row="1" Grid.RowSpan="2" VerticalAlignment="Bottom">
<ListView x:Name="ProgressListView" ItemsSource="{Binding ProgressCollection}" Background="Transparent" Canvas.Bottom="0" BorderThickness="0">
</ListView>
</Canvas>
This produces some what the result i was expecting. The progress controls is now fully responsive as I am no longer disabling the hittest. And the controls underneath the canvas is responding to mouse events. The reason this works is that the listview control is almost hidden when no progresses are shown. I tiny fraction of the ui in the corner is where the listview is. I could probably enable and disable the hittest in those conditions, but for now i think it is fine.
The reason i am calling this a work around is that when progresses are shown, ei more that 1, the space between the progress controls is not really a space and the mouse event does not sip through to the underlying controls. But we are also talking about 3-4 pixels in width, so only if a user is trying to click between something that is blocking for 5 seconds, then this becomes a problem. I think this will work for now. I still have some styling to do, but the close button now works as expected.
modified 11-Aug-23 4:01am.
|
|
|
|
|
|
I edited my question. For the original question, you are correct. I can't. I found that out and also found the same stackoverflow link you have posted. So my revised question, is now: How can I make the button work on my progress unit and still have them floating?
|
|
|
|
|