UWP RequestedTheme is not consistent in ListView and ListViewItem - c#

My NowPlayFullPage has a PlaylistControl, which is basically a ListView.
<local:PlaylistControl
x:Name="FullPlaylistControl"
Margin="10,10,10,0"
AllowReorder="True"
AlternatingRowColor="False"
Background="Transparent"
IsNowPlaying="True"
RequestedTheme="Dark" />
The ItemTemplate of the PlaylistControl is the following:
<local:PlaylistControlItem
Data="{x:Bind}"
DataContext="{x:Bind}"
RequestedTheme="{Binding ElementName=PlaylistController, Path=RequestedTheme}"
ShowAlbumText="{Binding ElementName=PlaylistController, Path=ShowAlbumText}" />
And in the Loaded event of the PlaylistControlItem, I called a function SetTextColor
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
MediaHelper.SwitchMusicListeners.Add(this);
SetTextColor(MediaHelper.CurrentMusic);
}
public void SetTextColor(Music music)
{
if (Data == music)
{
TitleTextBlock.Foreground = ArtistTextButton.Foreground = AlbumTextButton.Foreground = DurationTextBlock.Foreground =
LongArtistTextButton.Foreground = LongArtistAlbumPanelDot.Foreground = LongAlbumTextButton.Foreground = ColorHelper.HighlightBrush;
TextColorChanged = true;
}
else if (TextColorChanged)
{
if (RequestedTheme == ElementTheme.Dark)
{
TitleTextBlock.Foreground = ColorHelper.WhiteBrush;
ArtistTextButton.Foreground = AlbumTextButton.Foreground = DurationTextBlock.Foreground =
LongArtistTextButton.Foreground = LongArtistAlbumPanelDot.Foreground = LongAlbumTextButton.Foreground = ColorHelper.GrayBrush;
}
else
{
TitleTextBlock.Foreground = ArtistTextButton.Foreground = AlbumTextButton.Foreground = DurationTextBlock.Foreground =
LongArtistTextButton.Foreground = LongArtistAlbumPanelDot.Foreground = LongAlbumTextButton.Foreground = ColorHelper.BlackBrush;
}
TextColorChanged = false;
}
}
My question is, why does the RequestedTheme in the SetTextColor called in the Loaded event have the value of ElementTheme.Default instead of ElementTheme.Dark? When does the RequestTheme of PlaylistControlItem hold the value of Dark so that my text color can be set correctly?

Advisably you should he handling this using ThemeResource's in XAML, not in code, see: https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/themeresource-markup-extension
But to answer your question this is expected behaviour. ElementTheme.Default simply means the element has not had it's theme overridden and is using the default app theme. The other two values mean the element has specifically had it's theme overridden. App.Current.RequestedTheme gives the application's actual theme. FrameworkElement.RequestedTheme will always have a value of Default unless you explicitly set it to something else on that specific element. All of it's children will still have the value Default.
Note, you should compare against ActualTheme, not RequestedTheme, as a parent may have caused it to be using a different theme than the application if it's value is still ElementTheme.Default.
A method like below might help you to get a proper value of Light/Dark.
public static ElementTheme GetEffectiveTheme(FrameworkElement e)
{
if (e.ActualTheme == ElementTheme.Default)
return App.Current.RequestedTheme == ApplicationTheme.Dark ? ElementTheme.Dark : ElementTheme.Light;
return e.ActualTheme;
}
But also, just use ThemeResources. They automatically re-evaluate on Theme change, no need for any code or event listeners.

Related

List of an object type that prevents backcodes ( like... tbControl.Text = "value" ) from working in asp

I have been working with these properties for a while and have never had this problem before. I have a property like this:
public List<AirlineTickets_DOL> lstAirlineTickets
{
get
{
if (!(ViewState["lstAirlineTickets"] is List<AirlineTickets_DOL>))
{
ViewState["lstAirlineTickets"] = new List<AirlineTickets_DOL>();
}
return (List<AirlineTickets_DOL>)ViewState["lstAirlineTickets"];
}
set
{
ViewState["lstAirlineTickets"] = (List<AirlineTickets_DOL>)value;
}
}
When the data is returned somehow inside the OnTextChanged event I have to fill it in as shown below :
protected void tbFlightNumber_Book_Ticket_TextChanged(object sender, EventArgs e)
{
AutoFillAirlineTicket(sender);
}
private void AutoFillAirlineTicket(object sender)
{
TextBox tbFlightNumber_Book_Ticket = ((Control)sender).NamingContainer.FindControl("tbFlightNumber_Book_Ticket") as TextBox;
TextBox tbFrom_Book_Ticket = ((Control)sender).NamingContainer.FindControl("tbFrom_Book_Ticket") as TextBox;
TextBox tbTo_Book_Ticket = ((Control)sender).NamingContainer.FindControl("tbTo_Book_Ticket") as TextBox;
FillFlightData(tbFlightNumber_Book_Ticket, tbDate_Book_Ticket, tbFrom_Book_Ticket, tbTo_Book_Ticket);
UpdatePanel upAirlineTicket = ((Control)sender).NamingContainer.FindControl("upAirlineTicket") as UpdatePanel;
upAirlineTicket.Update();
//List<AirlineTickets_DOL> lstAirlineTickets = new List<AirlineTickets_DOL>();
lstAirlineTickets.Add(new AirlineTickets_DOL
{
counter = (nCounter > 0 ? nCounter : 1),
FlightNumber = tbFlightNumber_Book_Ticket.Text,
From = tbFrom_Book_Ticket.Text,
To = tbTo_Book_Ticket.Text,
});
nCounter++;
ListView lstviewAirlineTickets = ((Control)sender).NamingContainer.FindControl("lstviewAirlineTickets") as ListView;
lstviewAirlineTickets.DataSource = lstAirlineTickets;
lstviewAirlineTickets.DataBind();
}
When I remove the comment, the FillFlightData function fills the controls (TextBoxes), but when using the property as I explained above, the process of filling the fields does not work and does not give any output on the browser.
If you need more explain just tell me. I will be happy if someone help.
I found the solution, I changed the event to normal onClick Event and make sure the class had to be defined as [Serializable].

Update ThemeResources from c#?

I've got a void that analyses an image, extracts two dominant colors, and replaces "SystemControlForegroundAccentBrush" and "SystemControlHighlightAccentBrush", that I'm overriding in App.xaml. It works well, except that it takes a bit of time to analyse the image, so it changes the colors once all the controls in my page have already loaded.
As a result, they stay the old accent color. How can I get them to bind dynamically to that ThemeRessource?
Here's my app.xaml:
<Application.Resources>
<ResourceDictionary x:Name="resdic">
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="SystemControlForegroundAccentBrush"/>
<SolidColorBrush x:Key="SystemControlHighlightAccentBrush"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Application.Resources>
This is the (very simplified) part of the ColorExtractor.cs class that changes the colors:
public async static void Analyse()
{
Application.Current.Resources["SystemControlForegroundAccentBrush"] = new SolidColorBrush(color);
Application.Current.Resources["SystemControlHighlightAccentBrush"] = new SolidColorBrush(color2);
}
And I've got a bunch of controls in Page.xaml that have their Foreground set as such:
<TextBlock Foreground="{ThemeResource SystemControlForegroundAccentBrush]"/>
I call ColorExtractor.Analyse() in my Page.xaml.cs (at the OnNavigatedTo event). I can always create an event that gets fired once the colors are set, but I need to find a way to update the colors of all the controls in my page once that's done.
You can have a look at how Template 10 does theming changes, but in their case they are defining two different themes in resource dictionaries in advance. You can find their code in the repo on Github, but here are some of the code used:
(Window.Current.Content as FrameworkElement).RequestedTheme = value.ToElementTheme();
Views.Shell.SetRequestedTheme(value, UseBackgroundChecked);
From here
public static void SetRequestedTheme(ApplicationTheme theme, bool UseBackgroundChecked)
{
WindowWrapper.Current().Dispatcher.Dispatch(() =>
{
(Window.Current.Content as FrameworkElement).RequestedTheme = theme.ToElementTheme();
ParseStyleforThemes(theme);
HamburgerMenu.NavButtonCheckedForeground = NavButtonCheckedForegroundBrush;
HamburgerMenu.NavButtonCheckedBackground = (UseBackgroundChecked) ?
NavButtonCheckedBackgroundBrush : NavButtonBackgroundBrush;
HamburgerMenu.NavButtonCheckedIndicatorBrush = (UseBackgroundChecked) ?
Colors.Transparent.ToSolidColorBrush() : NavButtonCheckedIndicatorBrush;
HamburgerMenu.SecondarySeparator = SecondarySeparatorBrush;
List<HamburgerButtonInfo> NavButtons = HamburgerMenu.PrimaryButtons.ToList();
NavButtons.InsertRange(NavButtons.Count, HamburgerMenu.SecondaryButtons.ToList());
List<HamburgerMenu.InfoElement> LoadedNavButtons = new List<HamburgerMenu.InfoElement>();
foreach (var hbi in NavButtons)
{
StackPanel sp = hbi.Content as StackPanel;
if (hbi.ButtonType == HamburgerButtonInfo.ButtonTypes.Literal) continue;
ToggleButton tBtn = sp.Parent as ToggleButton;
Button btn = sp.Parent as Button;
if (tBtn != null)
{
var button = new HamburgerMenu.InfoElement(tBtn);
LoadedNavButtons.Add(button);
}
else if (btn != null)
{
var button = new HamburgerMenu.InfoElement(btn);
LoadedNavButtons.Add(button);
continue;
}
else
{
continue;
}
Rectangle indicator = tBtn.FirstChild<Rectangle>();
indicator.Visibility = ((!hbi.IsChecked ?? false)) ? Visibility.Collapsed : Visibility.Visible;
if (!hbi.IsChecked ?? false) continue;
ContentPresenter cp = tBtn.FirstAncestor<ContentPresenter>();
cp.Background = NavButtonCheckedBackgroundBrush;
cp.Foreground = NavButtonCheckedForegroundBrush;
}
LoadedNavButtons.ForEach(x => x.RefreshVisualState());
});
}
From here
I've got a void that analyses an image, extracts two dominant colors, and replaces "SystemControlForegroundAccentBrush" and "SystemControlHighlightAccentBrush", that I'm overriding in App.xaml.
First I don't think your code in app.xaml can override the ThemeResource, and you used this Brush in the Page like this:
<TextBlock Foreground="{ThemeResource SystemControlForegroundAccentBrush}" Text="Hello World!" FontSize="50" />
If you press "F12" on the SystemControlForegroundAccentBrush, you can find this resource actually in the "generic.xaml" file.
Now suppose your ColorExtractor.cs class works fine and ColorExtractor.Analyse() can override the color of those two brushes, and you have many controls in the page uses these two resources, refreshing the Page here can solve your problem.
But I think it's better that not put this operation in OnNavagateTo event or Page.Loaded event, there is no refresh method in UWP, we use navigating again to this page to refresh, so if putting this operation in OnNavagateTo event or Page.Loaded event, every time you navigate to this page, the resources will be overrided and it will navigate again. So I put this operation in a Button click event like this:
public bool Reload()
{
return Reload(null);
}
private bool Reload(object param)
{
Type type = this.Frame.CurrentSourcePageType;
if (this.Frame.BackStack.Any())
{
type = this.Frame.BackStack.Last().SourcePageType;
param = this.Frame.BackStack.Last().Parameter;
}
try { return this.Frame.Navigate(type, param); }
finally
{
this.Frame.BackStack.Remove(this.Frame.BackStack.Last());
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
ColorExtractor.Analyse();
Reload();
}
In the end, I decided to create an event in ColorExtractor.cs :
public static event EventHandler Analysed;
public async static void Analyse(BitmapImage poster)
{
//Analyse colors
Analysed(null, null);
}
And then, on my MainPage.xaml.cs:
ColorExtractor.Analyse(bmp);
ColorExtractor.Analysed += (sender, EventArgs) =>
{
//Set Page foreground color, as a lot of controls are dynamically binded to their parent's foreground brush.
//If a control isn't automatically binded, all I have to do is say: Foreground="{Binding Foreground, ElementName=page}"
page.Foreground = Application.Current.Resources["SystemControlForegroundAccentBrush"] as SolidColorBrush;
page.BorderBrush = Application.Current.Resources["SystemControlHighlightAccentBrush"] as SolidColorBrush;
//Reload any custom user control that sets it's children's color when it's loaded.
backdrop.UserControl_Loaded(null, null);
};
So I'm not actually binding my controls to the ForegroundAccentBrush directly, but this works without needing to re-navigate to the page.

How could I change the properties of a Control after the resource file has been applied?

I need to add a scroll bar for a component when a user changes their font size to 125% or 150%. To do this I added a method in the component, which sets the AutoScroll property to true.
protected override void OnSizeChanged(EventArgs e)
{
if (SystemFonts.DefaultFont.Size < 8)
{
this.AutoScroll = true;
}
if (this.Handle != null)
{
this.BeginInvoke((MethodInvoker) delegate
{
base.OnSizeChanged(e);
});
}
}
This works well, but one of the components should not get a scrollbar.
The above method will be triggered when initializing the controllers like this:
this.ultraExpandableGroupBoxPanel1.Controls.Add(this.pnlViewMode);
this.ultraExpandableGroupBoxPanel1.Controls.Add(this.ucASNSearchCriteria);
resources.ApplyResources(this.ultraExpandableGroupBoxPanel1, "ultraExpandableGroupBoxPanel1");
this.ultraExpandableGroupBoxPanel1.Name = "ultraExpandableGroupBoxPanel1";
The method will be triggered when adding into Controls and after this, the resource will be applied. The component which I don't want to change belongs to ucASNSearchCriteria in above code.
Now I want to set the AutoScroll property of 'ucASNSearchCriteria' to false after the resource has been applied. I have little knowledge about the rendering process of c# ui controls. Is it possible to dynamically change properties after applying?
I'd create a derived control of the desired type and add a property AllowAutoScroll or anything like that with the default value true.
With this, you can change that property in the WinForms designer easily and react on that property as the size gets changed.
So the designer will add this line of code for you if you change it to be non-default (false):
this.ucASNSearchCriteria.AllowAutoScroll = false;
... and you can react on that new property like this:
protected override void OnSizeChanged(EventArgs e)
{
if (AllowAutoScroll)
{
if (SystemFonts.DefaultFont.Size < 8)
{
this.AutoScroll = true;
}
if (this.Handle != null)
{
this.BeginInvoke((MethodInvoker) delegate
{
base.OnSizeChanged(e);
});
}
}
}

C# label should do diffrent things when clicked

I need a label which does something different with every click.
private void open_Click(object sender, EventArgs e)
{
for (int i = 0; i < 5; i++)
{
builder = new StringBuilder(4);
builder.Append(zahl1.Text);
builder.Append(zahl2.Text);
builder.Append(zahl3.Text);
builder.Append(zahl4.Text);
code = builder.ToString();
}
if( code== setCode)
{
openAndClose.BackColor = Color.DarkGreen;
setNewCode.Visible = true;
}
else
{
}
}
With the first click the BackColor gets green and the visible is true.
And now it should go back in the start position if I click it again.
That means BackColor should be red and the visible should be false.
Can I do this wth a second Eventhandler?
openAndClose.Click += new EventHandler(open_Click);
Thanks
You could simply do the following:
if(code == setCode)
{
openAndClose.BackColor = openAndClose.BackColor == Color.DarkGreen ? Color.Red : Color.DarkGreen;
setNewCode.Visible = !setNewCode.Visible;
}
The first part toggles the color between green and red, and the second part toggles the visibility.
You should be able to get what you want by having a global field which denotes if the label has been clicked before hand or not.
In short, initially set your flag to false, do something like so:
EventHandler()
{
if(!flag)
{
BackColour = Green
Visible = true
}
else
{
BackColour = Red
Visible = false
}
flag = !flag
}
Attaching multiple event handlers will simply invoke multiple event handlers each time.
I think you do not even need a extra boolean or the check. You can just check if visible then hide, if not visible make it appear

LookUpEdit not selecting newly entered value when it's a double

I have 2 LookUpEdit controls from DevExpress on my form. Both use an ObservableCollection as it's datasource, one being of type string and the other of type double. The LookUpEdit control has an event called ProcessNewValue which fires when, you guessed it, a new value is entered in the control. I've added some code in this event to add the newly added value to the ObservableCollection and it automatically selects it once done. This works as expected for the string LooUpEdit but when I try it with the double LookUpEdit`, it adds it to the collection but then it clears out the control.
Here's the code to load the controls, which gets called in Form_Load():
void InitControls()
{
double[] issueNumbers = new double[5];
issueNumbers[0] = 155;
issueNumbers[1] = 156;
issueNumbers[2] = 157;
issueNumbers[3] = 158;
issueNumbers[4] = 159;
ObservableCollection<double> issues = new ObservableCollection<double>(issueNumbers);
lookupIssues.Properties.DataSource = issues;
DevExpress.XtraEditors.Controls.LookUpColumnInfoCollection colInfo = lookupIssues.Properties.Columns;
colInfo.Clear();
colInfo.Add(new DevExpress.XtraEditors.Controls.LookUpColumnInfo("Column"));
colInfo[0].Caption = "Issue ID's";
string[] stringNumbers = Array.ConvertAll<double, string>(issueNumbers, Convert.ToString);
ObservableCollection<string> issuesString = new ObservableCollection<string>(stringNumbers);
lookupStringValue.Properties.DataSource = issuesString;
colInfo.Clear();
colInfo.Add(new DevExpress.XtraEditors.Controls.LookUpColumnInfo("Column"));
colInfo[0].Caption = "String Issue ID's";
}
And here's the ProcessNewValue event for both (I've renamed them to try to make it easier to see which does what):
private void OnProcessNewValue_Double(object sender, DevExpress.XtraEditors.Controls.ProcessNewValueEventArgs e)
{
ObservableCollection<double> source = (ObservableCollection<double>)(sender as LookUpEdit).Properties.DataSource;
if (source != null)
{
if ((sender as LookUpEdit).Text.Length > 0)
{
source.Add(Convert.ToDouble((sender as LookUpEdit).Text));
(sender as LookUpEdit).Refresh();
}
}
e.Handled = true;
}
private void OnProcessNewValue_String(object sender, DevExpress.XtraEditors.Controls.ProcessNewValueEventArgs e)
{
ObservableCollection<string> source = (ObservableCollection<string>)(sender as LookUpEdit).Properties.DataSource;
if (source != null)
{
if ((sender as LookUpEdit).Text.Length > 0)
{
source.Add((sender as LookUpEdit).Text);
(sender as LookUpEdit).Refresh();
}
}
e.Handled = true;
}
As you can see, the code it identical with the exception of one converting text to a double before adding it to the collection.
Anyone know why the double value gets added to the collection but the control doesn't automatically select it like it does with a string collection? I've even tried to hard-code the newly added value right after e.Handled = true; but it still doesn't select it. What's weird is that if I run it through the debugger, I can step through and see that the lookupIssues control indeed gets the newly added value AND it's Text property is set to it, but as soon as the event terminates, the control clears it out.....really strange.
Any help is greatly appreciated!
BTW, I can add a link to a sample project that duplicates the problem but you would need to have DevExpress v12.2.6 controls installed in order to compile the project.
I posted this to the DevExpress team as well and they were gracious enough to provide the solution:
I agree that this discrepancy appears confusing as-is. The reason for the discrepancy is LookUpEdit.ProcessNewValueCore makes a call to RepositoryItemLookUpEdit.GetKeyValueByDisplayValue which returns a null value from the LookUpListDataAdapter because no implicit conversion exists from double to string. You may resolve the discrepancy with the following change to your ProcessNewValue handler:
private void OnProcessNewValue_Double(object sender, DevExpress.XtraEditors.Controls.ProcessNewValueEventArgs e)
{
ObservableCollection<double> source = (ObservableCollection<double>)(sender as LookUpEdit).Properties.DataSource;
if (source != null) {
if ((sender as LookUpEdit).Text.Length > 0) {
double val = Convert.ToDouble((sender as LookUpEdit).Text);
source.Add(val);
e.DisplayValue = val;
(sender as LookUpEdit).Refresh();
}
}
e.Handled = true;
}
The control now behaves as expected. I hope this can help someone else out :)

Categories