Is there a way to freeze the first tab of a C#.net tab control?
I have a tab controller which can have many tabs. If the user is scrolling them the first tab should remain in the first position and rest of the tabs should move.
I have tried to do it using removing and inserting the tab in paint method. But seems like it is getting index issues some times when I tried to remove and add the first page.
// For the first time home tab comes as the first tab item
if (this.homeTab == null)
{
this.homeTab = this.TabPages[0];
}
// get initial first display index to a temp variable
int tempFirstIndex = -1;
for (int index = 0; index < this.TabCount; index++)
{
Rectangle currentTabBounds = this.GetTabTextRect(index);
if (currentTabBounds != Rectangle.Empty && tempFirstIndex < 0 && currentTabBounds.X >= 0)
{
tempFirstIndex = index;
break;
}
}
int homeTabIndex = this.TabPages.IndexOf(this.homeTab);
Rectangle homeTabBounds = this.GetTabTextRect(homeTabIndex);
if (homeTabIndex > tempFirstIndex)
{
this.TabPages.Remove(this.homeTab);
this.TabPages.Insert(tempFirstIndex, this.homeTab);
}
else
{
// find the first visible position
// it can not be simply the tempFirstIndex, because in this scenario tab is removed from begining
// tabs are not same in width
while (homeTabBounds != Rectangle.Empty && homeTabBounds.X < 0)
{
homeTabIndex++;
this.TabPages.Remove(this.homeTab);
this.TabPages.Insert(homeTabIndex, this.homeTab);
homeTabBounds = this.GetTabTextRect(homeTabIndex);
}
}
I have figured out a way to freeze the first tab.
The problem with the above solution is, I tried to manipulate the tab page list inside the paint method. It causes to call the paint method over and over again and generates exceptions.
So I have tried to manipulate the tab position returned from "this.GetTabTextRect(index)". It worked fine. But I had to write some code to adjust the tab selection logic as well.
protected Rectangle GetTabTextRect(int index)
{
// gets the original tab position
Rectangle tabBounds = this.GetTabRect(index);
// first displayed index will be calculated in the paint method
// if it is equal or less than to zero means, the number of tabs are not exceeded the display limit. So no need tab manipulating
if (this.firstDisplayedIndex > 0 )
{
// if it is not first tab we adjust the position
if (index > 0)
{
if (index > this.firstDisplayedIndex && index <= this.lastDisplayedIndex)
{
Rectangle prevBounds = this.CurrentTabControl.GetTabRect(this.firstDisplayedIndex);
// home tab (first tab) bounds will be stored in a global variable when the tab control initializes
tabBounds.X = tabBounds.X - prevBounds.Width + this.homeTabBounds.Width;
}
else if (index == this.firstDisplayedIndex) // we need to free this slot for the first tab (index = 0)
{
tabBounds.X -= 1000;
}
}
else
{
// first tab position is fixed
tabBounds.X = 0;
}
}
}
Related
I have a RichTextBox. I'm trying to code so that if a specific color is found in the SelectionBackColor property of the RTB, the background color of the words/ texts to be removed. For that, I need to detect if there exists multiple colors in the RTB. However, according to the documentation,
If the current text selection has more than one color specified, this property returns Color.Empty.
This is what I've tried so far:
private void randomBtn_Click(object sender, EventArgs e)
{
int startIndex = 0; //start from beginning of the richTextBox1
int endIndex = this.richTextBox1.TextLength; //until the end of all text available
this.richTextBox1.Select(startIndex, endIndex); //select from start until the end
if(endIndex != 0)
{
for(int i=startIndex; i< endIndex; i++)
{
if (this.richTextBox1.Text[i].ToString().Contains(" ")) //skips white spaces
{
continue;
}
else
{
while ((this.richTextBox1.BackColor != Color.Empty))
{
if (this.richTextBox1.SelectionBackColor.R == 155 && this.richTextBox1.SelectionBackColor.G == 255 && this.richTextBox1.SelectionBackColor.B == 255)
{
this.richTextBox1.HideSelection = true; //to prevent text highlighted
MessageBox.Show(this, "Texts with RGB(155, 255, 255) found!", "", MessageBoxButtons.OK);
break;
}
else
{
this.richTextBox1.HideSelection = true;
MessageBox.Show(this, "Error!", "", MessageBoxButtons.OK);
break;
}
}
}
}
}
else
{
MessageBox.Show(this, "richTextBox1 is empty!", "Alert!", MessageBoxButtons.OK);
}
}
To test, I added a breakpoint at the code containing the while line. Below shows the success and fail criterion,
The code works if:
There is no whitespace(at all)
There is only one color in the RTB
The code fails if:
There are whitespaces in between texts/ words/ characters
There are multiple colors in the RTB
Below shows the program execution examples:
This is when only one color is applied in the RTB(success),
This is when only one color and a whitespace are applied in the RTB(fail),
This is when multiple colors and whitespaces are applied in the RTB(fail),
So, is there any ways to override the return value of the SelectionColor property to be able to detect multiple colors, or are there any other ways of doing this? Just so you know, I've searched for this kind of problem over the internet, but I didn't think I've find any related issues.
It took me a while to figure out what #TaW meant in the comment section, but thanks to him, I've managed to solve this issue.
Actually, based on my reply in the comment section that I asked #TaW, what I thought as the same concept, was actually wrong. In the post above, what I did was entirely wrong:
I was supposed to assess each text one by one to know what their colors are. However, the codes below were already wrong to begin with.:
int startIndex = 0;
int endIndex = this.richTextBox1.TextLength;
this.richTextBox1.Select(startIndex, endIndex);
To analyze how RTB.SelectionColor, RTB.SelectionStart, and RTB.SelectionLength work, I decided to create another project. The project is a simple program containing an RTB, and some other buttons to manage the RTB's SelectionColor. If you want to check for the project that I've done, you're always welcomed to visit this link.
From "2", I re-used all the codes to suit the original project that I was working on. Now, it's all fine and working as it should.
To note, there are two important concepts/ ideas on managing the Selection property of the RTB.
If you are concerned of assessing each and every text in the RTB, you should code it like this:
private void methodA(RichTextBox localRTB)
{
//go through text one by one
int startIndex = 0;
int endIndex = localRTB.TextLength;
localRTB.SelectionStart = startIndex;
localRTB.SelectionLength = 1; //always 1 'cuz we want to assess each text one by one
while (localRTB.SelectionStart < endIndex)
{
//if the SelectionBackColor is red, change it to white
if (localRTB.SelectionBackColor == Color.Red) //take red color for example
{
localRTB.SelectionBackColor = Color.White;
}
//--manage bg color of selected text in RTB--
//so that able to go to next text
localRTB.SelectionStart += 1;
}
//finally...
localRTB.DeselectAll(); //unselect text in RTB
localRTB.Select(); //set focus back to the RTB
}
Below shows the result of the codes above(1):
If you don't really care about assessing each and every text in the RTB, you should code it like this instead:
private void methodB(RichTextBox localRTB)
{
int startIndex;
int endIndex;
if (localRTB.SelectionLength.Equals(0)) //if user don't select any text
{
startIndex = 0; //from beginning of RTB
endIndex = localRTB.TextLength; //'til the end of RTB
//--manage selected text in the RTB--
localRTB.SelectionStart = startIndex;
localRTB.SelectionLength = endIndex;
localRTB.Select(localRTB.SelectionStart, localRTB.SelectionLength);
//--manage selected text in the RTB--
}
else if (!(localRTB.SelectionLength.Equals(0))) //if user has text selected
{
startIndex = localRTB.SelectionStart; //from beginning of RTB
endIndex = localRTB.SelectionLength; //'til the end of RTB
if (localRTB.SelectedText.Contains(" ")) //skips whitespaces if selected together with text
{
if (localRTB.SelectedText.EndsWith(" "))
{
endIndex -= 1;
}
}
//--manage selected text in the RTB--
localRTB.Select(startIndex, endIndex);
//--manage selected text in the RTB--
}
}
Below shows the result of the codes above(2):
Peace...✌️
I am trying to pass the index of the clicked button in Repeat(int value) and wrote this -
gp.GetComponentInChildren ().onClick.AddListener(() =>
Repeat(rep));
But when I click any button I got the last index of the button for all.
I want to know is there any way, I can pass the index of that button which i clicked in Repeat()?
void chatDialogs() {
foreach (Transform child in this.transform) {
GameObject.Destroy (child.gameObject);
}
for (int i = 5; i > 0 ; i--) {
int currentStep = Laststep - i;
if (currentStep >= 0) {
gp = (GameObject)Instantiate (playerPreFab);
gp.transform.SetParent (this.transform);
}
gp.GetComponentInChildren<Button> ().onClick.AddListener(() =>
Repeat(**transform.GetSiblingIndex()**));
}
public void Repeat(int speakstep) {
Application.ExternalCall("textspeak", speakstep);
}
in speakstep object of Repeat() the clicked button index should be passed, but its getting the last index in every button I click.
I think you can do something like this:
AddButtonListener(gp.GetComponentInChildren<Button> (), i);
inside your for loop,
when AddButtonListener is defined as:
void AddButtonListener(Button b, int index)
{
b.onClick.AddListener(()=>{Repeat(index)});
}
This way you capture the button index in the listener function (but I'm not sure what the correct name of this pattern is), and I wrote this without actually running it. Hope you have enough info now to get it working.
I have a Pivot with several PivotItems, one of which contains a canvas that places its items in dynamic locations (depending on the data). I get the data, and I can place the items in their place before the user can choose this item (this isn't the first pivot). However, only when I select the PivotItem, the canvas renders itself, so you can see it flicker before it's shown as it should.
Is there a way to force the canvas to render before it's shown, so everything's prepared by the time the user sees it?
My code looks something like this:
In the page.xaml.cs:
private async void GameCenterView_OnDataContextChanged(object sender, EventArgs e)
{
// Load data...
// Handle other pivots
// This is the problem pivot
if (ViewModel.CurrentGame.SportTypeId == 1)
{
_hasLineups = ViewModel.CurrentGame.HasLineups.GetValueOrDefault();
HasFieldPositions = ViewModel.CurrentGame.HasFieldPositions.GetValueOrDefault();
// I only add the pivot when I need it, otherwise, it won't be shown
if (_hasLineups)
{
if (MainPivot.Items != null) MainPivot.Items.Add(LineupPivotItem);
}
if (HasFieldPositions)
{
// Here I place all the items in their proper place on the canvas
ArrangeLineup(ViewModel.TeamOneLineup, TeamOneCanvas);
ArrangeLineup(ViewModel.TeamTwoLineup, TeamTwoCanvas);
}
}
// Handle other pivots
}
private void ArrangeLineup(ObservableCollection<PlayerInLineupViewModel> teamLineup, RationalCanvas canvas)
{
if (teamLineup == null)
return;
foreach (var player in teamLineup)
{
var control = new ContentControl
{
Content = player,
ContentTemplate = LinupPlayerInFieldDataTemplate
};
control.SetValue(RationalCanvas.RationalTopProperty, player.Player.FieldPositionLine);
control.SetValue(RationalCanvas.RationalLeftProperty, player.Player.FieldPositionSide);
canvas.Children.Add(control);
}
}
The canvas isn't the stock canvas. I created a new canvas that displays items according to their relative position (I get the positions in a scale of 0-99).
The logic happens in the OverrideArrange method:
protected override Size ArrangeOverride(Size finalSize)
{
if (finalSize.Height == 0 || finalSize.Width == 0)
{
return base.ArrangeOverride(finalSize);
}
var yRatio = finalSize.Height/100.0;
var xRatio = finalSize.Width/100.0;
foreach (var child in Children)
{
var top = (double) child.GetValue(TopProperty);
var left = (double) child.GetValue(LeftProperty);
if (top > 0 || left > 0)
continue;
var rationalTop = (int) child.GetValue(RationalTopProperty);
var rationalLeft = (int) child.GetValue(RationalLeftProperty);
if (InvertY)
rationalTop = 100 - rationalTop;
if (InvertX)
rationalLeft = 100 - rationalLeft;
child.SetValue(TopProperty, rationalTop*yRatio);
child.SetValue(LeftProperty, rationalLeft*xRatio);
}
return base.ArrangeOverride(finalSize);
}
Thanks.
There are several tricks you could try. For example:
In your ArrangeOverride you can short-circuit the logic if the size hasn't changed since last time you executed (and the data is the same)
Make sure you're listening to the events on Pivot that tell you to get ready for presentation - PivotItemLoading for example
You can have the control not actually be part of the Pivot, but instead be in the parent container (eg a Grid) and have it with Opacity of zero. Then set it to 100 when the target PivotItem comes into view.
I am using Xamarin and C# but I suspect the problem is equally valid in a Java environment.
I have an ActionBar Activity that hosts three tabs each of which hosts a fragment. It uses a ViewPager to allow the user to swipe between the tabs.
The requirement is to programmatically screenshot each tab and then email these as attachments.
The problem is that whilst the ActionBar/ViewPager works well it also optimises the tabs - effectively it isn't creating a fragment's view until it is next in line to be shown. So, if you're on tab 0 - the first tab - then the fragment view for tab 2 is null. So it can't be screenshot.
To overcome this I have tried to set any tab/fragment that has a null view to be selected. This generates the view but because setting it to be selected does not actually render it on screen the view does not have a width or a height value so again it cannot be screenshot (this is the reason for the defensive check at the start of the code taking the screenshot).
So, I guess my question is how can I force the tab to be rendered on screen so that it is correctly filled out and can be screenshot?
My main code extracts are as follows:
private void EmailReport()
{
List <Bitmap> bitmaps = new List<Bitmap>();
List <string> summaryFiles = new List<string>();
// remember the tab we're on
var selectedTab = this.ActionBar.SelectedNavigationIndex;
// take the screenshots
for (int fragmentNumber = 0; fragmentNumber < projectFragmentPagerAdapter.Count; fragmentNumber++)
{
Android.Support.V4.App.Fragment fragment = projectFragmentPagerAdapter.GetItem(fragmentNumber);
if (fragment.View == null)
{
this.ActionBar.GetTabAt(fragmentNumber).Select();
fragment = projectFragmentPagerAdapter.GetItem(fragmentNumber);
}
bitmaps.Add(ScreenShot(fragment.View));
}
// set the active tab back
this.ActionBar.GetTabAt(selectedTab).Select();
//write the screenshots into file
int i = 0;
foreach(Bitmap bitmap in bitmaps)
{
if (bitmap != null)
summaryFiles.Add(BitmapToFile(bitmap, this.ActionBar.GetTabAt(i).Text));
i++;
}
// now send the file
EmailSupport.SendAttachments(this, summaryFiles);
}
private Bitmap ScreenShot(View fragmentRootView)
{
if (fragmentRootView == null || fragmentRootView.Width == 0 || fragmentRootView.Height == 0)
return null;
fragmentRootView.DrawingCacheEnabled = true;
//create a bitmap for the layout and then draw the view into it
Bitmap bitmap = Bitmap.CreateBitmap(fragmentRootView.Width, fragmentRootView.Height,Bitmap.Config.Argb8888);
Canvas canvas = new Canvas(bitmap);
//Get the view's background
Drawable bgDrawable = fragmentRootView.Background;
if (bgDrawable!=null) // has background drawable, then draw it on the canvas
bgDrawable.Draw(canvas);
else // does not have background drawable, then draw white background on the canvas
canvas.DrawColor(Color.White);
// draw the view on the canvas
fragmentRootView.Draw(canvas);
fragmentRootView.DrawingCacheEnabled = false;
return bitmap;
}
Any help would be gratefully received.
The solution in the end was very simple. The ViewPager has a setting controlling the number of pages (fragments) that it will hold "activated". This defaults to 1. As I had 3 tabs this meant there was always one tab (fragment) out of reach.
So, whilst setting up the ViewPager do the following before the tabs are added:
reportViewPager.OffscreenPageLimit = pageCount - 1;
Or in Java
reportViewPager.setOffscreenPageLimit(pageCount - 1);
I hope this helps someone else avoid wasting hours.
I have a ListView with MultiSelect = false, View = Details and CheckBoxes = True. I'm stepping through it and controlling visibility in an application. I'm currently just using the Else portion of the code below. But it doesn't account for the first Item being selected, it just turns the second item on. And whether or not the item is checked (already visible), and it turns the visibility off. I'm comparing elements associated with the items against elements that are already visible. My app crashes at the currentItem.Checked loop. And doesn't account for combinations (first and checked). How could I code this?
int indexCount = listView1.Items.Count;
ListViewItem currentItem = listView1.SelectedItems[0];
int currentIndex = currentItem.Index;
if (currentItem.Index == 0)
{
//listView1.SelectedItems[0] on
}
if (currentItem.Index == indexCount)
{
//end
}
if (currentItem.Checked == true)
{
while (currentItem.Checked == true)
{
listView1.SelectedIndices.Clear();
listView1.SelectedIndices.Add(currentIndex + 1);
}
//listView1.SelectedItems[0] on
}
else
{
//listView1.SelectedItems[0] off
listView1.SelectedIndices.Clear();
listView1.SelectedIndices.Add(currentIndex + 1);
//listView1.SelectedItems[0] on
}
It is not clear what you are trying to do.
You are basically picking one selected item and placing it in 'currentItem'. If that item is checked as true you are looping through it until? and why?